Skip to content

Commit

Permalink
Improve load time (#85)
Browse files Browse the repository at this point in the history
* Lazy import of various packages for better load time
* Add load time check
Co-authored-by: prisae <mail@werthmuller.org>
  • Loading branch information
banesullivan authored Jul 23, 2022
1 parent 4c95f5a commit 48d0a7f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 57 deletions.
44 changes: 7 additions & 37 deletions scooby/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,11 @@
It contains, for instance, known odd locations of version information for
particular modules (``VERSION_ATTRIBUTES``, ``VERSION_METHODS``)
It also checks and stores mandatory additional information, if possible, such
as available RAM or MKL info.
"""
import os
from pathlib import Path
import platform
import sys
import sysconfig

try:
import psutil
except ImportError:
psutil = False

try:
import mkl

mkl.get_version_string()
except (ImportError, AttributeError):
mkl = False

try:
import numexpr
except ImportError:
numexpr = False

# Get available RAM, if available
if psutil:
tmem = psutil.virtual_memory().total
TOTAL_RAM = '{:.1f} GiB'.format(tmem / (1024.0**3))
else:
TOTAL_RAM = False

# Get mkl info from numexpr or mkl, if available
if mkl:
MKL_INFO = mkl.get_version_string()
elif numexpr:
MKL_INFO = numexpr.get_vml_version()
else:
MKL_INFO = False

# Define unusual version locations
VERSION_ATTRIBUTES = {
'vtk': 'VTK_VERSION',
Expand Down Expand Up @@ -229,6 +192,13 @@ def meets_version(version, meets):

def get_filesystem_type():
"""Get the type of the file system at the path of the scooby package."""
try:
import psutil # lazy-load see PR#85
except ImportError:
psutil = False
from pathlib import Path # lazy-load see PR#85
import platform # lazy-load see PR#85

# Skip Windows due to https://github.com/banesullivan/scooby/issues/75
if psutil and platform.system() != 'Windows':
# Code by https://stackoverflow.com/a/35291824/10504481
Expand Down
86 changes: 66 additions & 20 deletions scooby/report.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"""The main module containing the `Report` class."""

import importlib
import multiprocessing
import platform
import sys
import textwrap
import time
from types import ModuleType

from .knowledge import (
MKL_INFO,
TOTAL_RAM,
VERSION_ATTRIBUTES,
VERSION_METHODS,
get_filesystem_type,
Expand All @@ -34,40 +29,82 @@ def system(self):
E.g. ``'Linux'``, ``'Windows'``, or ``'Java'``. An empty string is
returned if the value cannot be determined.
"""
return platform.system()
return platform().system()

@property
def platform(self):
"""Return the platform."""
return platform.platform()
return platform().platform()

@property
def machine(self):
"""Return the machine type, e.g. 'i386'.
An empty string is returned if the value cannot be determined.
"""
return platform.machine()
return platform().machine()

@property
def architecture(self):
"""Return the bit architecture used for the executable."""
return platform.architecture()[0]
return platform().architecture()[0]

@property
def cpu_count(self):
"""Return the number of CPUs in the system."""
return multiprocessing.cpu_count()
if not hasattr(self, '_cpu_count'):
import multiprocessing # lazy-load see PR#85

self._cpu_count = multiprocessing.cpu_count()
return self._cpu_count

@property
def total_ram(self):
"""Return total RAM info.
If not available, returns 'unknown'.
"""
if TOTAL_RAM:
return TOTAL_RAM
return 'unknown'
if not hasattr(self, '_total_ram'):

try:
import psutil # lazy-load see PR#85

tmem = psutil.virtual_memory().total
self._total_ram = '{:.1f} GiB'.format(tmem / (1024.0**3))
except ImportError:
self._total_ram = 'unknown'

return self._total_ram

@property
def mkl_info(self):
"""Return MKL info.
If not available, returns 'unknown'.
"""
if not hasattr(self, '_mkl_info'):
try:
import mkl # lazy-load see PR#85

mkl.get_version_string()
except (ImportError, AttributeError):
mkl = False

try:
import numexpr # lazy-load see PR#85

except ImportError:
numexpr = False

# Get mkl info from numexpr or mkl, if available
if mkl:
self._mkl_info = mkl.get_version_string()
elif numexpr:
self._mkl_info = numexpr.get_vml_version()
else:
self._mkl_info = 'unknown'

return self._mkl_info

@property
def date(self):
Expand Down Expand Up @@ -216,6 +253,8 @@ def __init__(

def __repr__(self):
"""Return Plain-text version information."""
import textwrap # lazy-load see PR#85

# Width for text-version
text = '\n' + self.text_width * '-' + '\n'

Expand Down Expand Up @@ -254,9 +293,9 @@ def __repr__(self):
text += f'{name:>{row_width}} : {version}\n'

# MKL details
if MKL_INFO:
if self.mkl_info != 'unknown':
text += '\n'
for txt in textwrap.wrap(MKL_INFO, self.text_width - 4):
for txt in textwrap.wrap(self._mkl_info, self.text_width - 4):
text += ' ' + txt + '\n'

# Finish
Expand Down Expand Up @@ -338,8 +377,8 @@ def cols(html, version, name, ncol, i):
html += " </tr>\n"

# MKL details
if MKL_INFO:
html = colspan(html, MKL_INFO, self.ncol, 2)
if self.mkl_info != 'unknown':
html = colspan(html, self.mkl_info, self.ncol, 2)

# Finish
html += "</table>"
Expand All @@ -360,7 +399,7 @@ def to_dict(self):
out['Architecture'] = self.architecture
if self.filesystem:
out['File system'] = self.filesystem
if TOTAL_RAM:
if self.total_ram != 'unknown':
out['RAM'] = self.total_ram
out['Environment'] = self.python_environment
for meta in self._extra_meta:
Expand All @@ -374,8 +413,8 @@ def to_dict(self):
out[name] = version

# MKL details
if MKL_INFO:
out['MKL'] = MKL_INFO
if self.mkl_info != 'unknown':
out['MKL'] = self.mkl_info

return out

Expand Down Expand Up @@ -445,3 +484,10 @@ def get_version(module):

# If not found, return VERSION_NOT_FOUND
return name, VERSION_NOT_FOUND


def platform():
"""Return platform as lazy load; see PR#85."""
import platform

return platform
16 changes: 16 additions & 0 deletions tests/test_scooby.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import subprocess
import sys

from bs4 import BeautifulSoup
Expand Down Expand Up @@ -170,3 +171,18 @@ def test_import_os_error():
with pytest.raises(OSError):
import pyvips # noqa
assert scooby.Report(['pyvips'])


@pytest.mark.skipif(not sys.platform.startswith('linux'), reason="Not Linux.")
def test_import_time():
# Relevant for packages which provide a CLI:
# How long does it take to import?
cmd = ["time", "-f", "%U", "python", "-c", "import scooby"]
# Run it twice, just in case.
subprocess.run(cmd)
subprocess.run(cmd)
# Capture it
out = subprocess.run(cmd, capture_output=True)

# Currently we check t < 0.15 s.
assert float(out.stderr.decode("utf-8")[:-1]) < 0.15

0 comments on commit 48d0a7f

Please sign in to comment.