diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000000000000000000000000000000000..2534a45ee555bb5ebefd37210d19399634c7a4ee --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 80 +ignore = E501,W503,E302,E402,E203 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6352dfc534c96dd6ea1c8bc7c061f4745b199881 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/timothycrosley/isort + rev: 5.10.1 + hooks: + - id: isort + args: [--settings-path, "pyproject.toml"] + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 + hooks: + - id: check-ast + - id: check-case-conflict + - id: trailing-whitespace + - id: end-of-file-fixer + - id: debug-statements + - id: check-added-large-files + - id: check-yaml + exclude: .*/meta.yaml diff --git a/LICENSE b/LICENSE index bd46ce15068f2d3b5a1b23ac6c68a33ec808d95d..0c3160331a4198b350981b4eb39a1b2f851813f6 100644 --- a/LICENSE +++ b/LICENSE @@ -24,4 +24,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bob/__init__.py b/bob/__init__.py index 2ab1e28b150f0549def9963e9e87de3fdd6b2579..edbb4090fca046b19d22d3982711084621bff3be 100644 --- a/bob/__init__.py +++ b/bob/__init__.py @@ -1,3 +1,4 @@ # see https://docs.python.org/3/library/pkgutil.html from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/bob/io/base/__init__.py b/bob/io/base/__init__.py index 80612baaf14c78a1d646b7d88b5ca65c25a3a6a1..3e4da95af2edb4f75239fe5f0239911091a39090 100644 --- a/bob/io/base/__init__.py +++ b/bob/io/base/__init__.py @@ -1,12 +1,12 @@ # import Libraries of other lib packages -import numpy as np +import logging + import h5py import imageio +import numpy as np from ..image import to_bob, to_matplotlib -import logging - logger = logging.getLogger(__name__) import os @@ -182,6 +182,7 @@ def load(inputs): """ from collections.abc import Iterable + import numpy if _is_string(inputs): @@ -248,44 +249,6 @@ read = load # open = File -def get_include_directories(): - """get_include_directories() -> includes - - Returns a list of include directories for dependent libraries, such as HDF5. - This function is automatically used by - :py:func:`bob.extension.get_bob_libraries` to retrieve the non-standard - include directories that are required to use the C bindings of this library - in dependent classes. You shouldn't normally need to call this function by - hand. - - **Returns:** - - ``includes`` : [str] - The list of non-standard include directories required to use the C bindings - of this class. For now, only the directory for the HDF5 headers are - returned. - """ - # try to use pkg_config first - try: - from bob.extension.utils import find_header - - # locate pkg-config on our own - header = "hdf5.h" - candidates = find_header(header) - if not candidates: - raise RuntimeError( - "could not find %s's `%s' - have you installed %s on this " - "machine?" % ("hdf5", header, "hdf5") - ) - - return [os.path.dirname(candidates[0])] - except RuntimeError: - from bob.extension import pkgconfig - - pkg = pkgconfig("hdf5") - return pkg.include_directories() - - def _generate_features(reader, paths, same_size=False): """Load and stack features in a memory efficient way. This function is meant to be used inside :py:func:`vstack_features`. diff --git a/bob/io/base/test/test_hdf5.py b/bob/io/base/test/test_hdf5.py index dde0a53e7cb1be00d17734265de6fbdb6b575c30..ec0e148ce0500e23765b76577919f6280bc11a98 100644 --- a/bob/io/base/test/test_hdf5.py +++ b/bob/io/base/test/test_hdf5.py @@ -8,12 +8,13 @@ """ import os -from bob.io.base import load, save -from ..test_utils import temporary_filename - +import random import numpy as np -import random + +from bob.io.base import load, save + +from ..test_utils import temporary_filename def read_write_check(data): diff --git a/bob/io/base/test/test_image_support.py b/bob/io/base/test/test_image_support.py index 08989569b2876163210e5069807a2895d360a1ae..04e1721581f0e535e1f9c4ded8a201de8c9644e7 100644 --- a/bob/io/base/test/test_image_support.py +++ b/bob/io/base/test/test_image_support.py @@ -9,14 +9,14 @@ """ import os -import numpy - -from bob.io.base import load, write -from ..test_utils import datafile, temporary_filename # import bob.io.image import nose +import numpy + +from bob.io.base import load, write +from ..test_utils import datafile, temporary_filename # These are some global parameters for the test. PNG_INDEXED_COLOR = datafile("img_indexed_color.png", __name__) @@ -148,7 +148,9 @@ def test_image_load(): # Testing exception nose.tools.assert_raises( - RuntimeError, lambda x: load(os.path.splitext(x)[0] + ".unknown"), full_file + RuntimeError, + lambda x: load(os.path.splitext(x)[0] + ".unknown"), + full_file, ) diff --git a/bob/io/base/test/test_io.py b/bob/io/base/test/test_io.py index b4ec9f684e3eda7e58b781e80f5c9e2a682e3b38..22b8fca959749fccdda33926e509d624693e64d6 100644 --- a/bob/io/base/test/test_io.py +++ b/bob/io/base/test/test_io.py @@ -1,7 +1,10 @@ +import os + import nose import numpy as np -import os -from bob.io.base import vstack_features, save, load + +from bob.io.base import load, save, vstack_features + from ..test_utils import temporary_filename diff --git a/bob/io/base/test_utils.py b/bob/io/base/test_utils.py index 8e83915644dd7821572fea0eb8f1c4b751f9a432..6d8f3a059f1048ba23c8e481a138718dc2735d8d 100644 --- a/bob/io/base/test_utils.py +++ b/bob/io/base/test_utils.py @@ -8,106 +8,113 @@ """Re-usable decorators and utilities for bob test code """ -import os import functools +import os + import nose.plugins.skip -def datafile(f, module=None, path='data'): - """datafile(f, [module], [data]) -> filename - Returns the test file on the "data" subdirectory of the current module. +def datafile(f, module=None, path="data"): + """datafile(f, [module], [data]) -> filename + + Returns the test file on the "data" subdirectory of the current module. + + **Parameters:** + + ``f`` : str + This is the filename of the file you want to retrieve. Something like ``'movie.avi'``. - **Parameters:** + ``module``: str + [optional] This is the python-style package name of the module you want to retrieve + the data from. This should be something like ``bob.io.base``, but you + normally refer it using the ``__name__`` property of the module you want to + find the path relative to. - ``f`` : str - This is the filename of the file you want to retrieve. Something like ``'movie.avi'``. + ``path``: str + [Default: ``'data'``] The subdirectory where the datafile will be taken from inside the module. + It can be set to ``None`` if it should be taken from the module path root (where the ``__init__.py`` file sits). - ``module``: str - [optional] This is the python-style package name of the module you want to retrieve - the data from. This should be something like ``bob.io.base``, but you - normally refer it using the ``__name__`` property of the module you want to - find the path relative to. + **Returns:** - ``path``: str - [Default: ``'data'``] The subdirectory where the datafile will be taken from inside the module. - It can be set to ``None`` if it should be taken from the module path root (where the ``__init__.py`` file sits). + ``filename`` : str + The full path of the file + """ - **Returns:** + resource = __name__ if module is None else module + final_path = f if path is None else os.path.join(path, f) + import pkg_resources - ``filename`` : str - The full path of the file - """ + return pkg_resources.resource_filename(resource, final_path) - resource = __name__ if module is None else module - final_path = f if path is None else os.path.join(path, f) - import pkg_resources - return pkg_resources.resource_filename(resource, final_path) +def temporary_filename(prefix="bobtest_", suffix=".hdf5"): + """temporary_filename([prefix], [suffix]) -> filename -def temporary_filename(prefix='bobtest_', suffix='.hdf5'): - """temporary_filename([prefix], [suffix]) -> filename + Generates a temporary filename to be used in tests, using the default ``temp`` directory (on Unix-like systems, usually ``/tmp``). + Please note that you are responsible for deleting the file after your test finished. + A common way to assure the file to be deleted is: - Generates a temporary filename to be used in tests, using the default ``temp`` directory (on Unix-like systems, usually ``/tmp``). - Please note that you are responsible for deleting the file after your test finished. - A common way to assure the file to be deleted is: + .. code-block:: py - .. code-block:: py + import bob.io.base.test_utils + temp = bob.io.base.test_utils.temporary_filename() + try: + # use the temp file + ... + finally: + if os.path.exist(temp): os.remove(temp) - import bob.io.base.test_utils - temp = bob.io.base.test_utils.temporary_filename() - try: - # use the temp file - ... - finally: - if os.path.exist(temp): os.remove(temp) + **Parameters:** - **Parameters:** + ``prefix`` : str + [Default: ``'bobtest_'``] The file name prefix to be added in front of the random file name - ``prefix`` : str - [Default: ``'bobtest_'``] The file name prefix to be added in front of the random file name + ``suffix`` : str + [Default: ``'.hdf5'``] The file name extension of the temporary file name - ``suffix`` : str - [Default: ``'.hdf5'``] The file name extension of the temporary file name + **Returns:** - **Returns:** + ``filename`` : str + The name of a temporary file that you can use in your test. + Don't forget to delete! - ``filename`` : str - The name of a temporary file that you can use in your test. - Don't forget to delete! + """ + import tempfile - """ - import tempfile - fd, name = tempfile.mkstemp(suffix, prefix) - os.close(fd) - os.unlink(name) - return name + fd, name = tempfile.mkstemp(suffix, prefix) + os.close(fd) + os.unlink(name) + return name def extension_available(extension): - '''Decorator to check if a extension is available before enabling a test + """Decorator to check if a extension is available before enabling a test - This decorator is mainly used to decorate a test function, in order to skip tests when the extension is not available. - The syntax is: + This decorator is mainly used to decorate a test function, in order to skip tests when the extension is not available. + The syntax is: - .. code-block:: py + .. code-block:: py - import bob.io.base.test_utils + import bob.io.base.test_utils - @bob.io.base.test_utils.extension_available('.ext') - def my_test(): - ... - ''' + @bob.io.base.test_utils.extension_available('.ext') + def my_test(): + ... + """ - def test_wrapper(test): + def test_wrapper(test): + @functools.wraps(test) + def wrapper(*args, **kwargs): + from . import extensions - @functools.wraps(test) - def wrapper(*args, **kwargs): - from . import extensions - if extension in extensions(): - return test(*args, **kwargs) - else: - raise nose.plugins.skip.SkipTest('Extension to handle "%s" files was not available at compile time' % extension) + if extension in extensions(): + return test(*args, **kwargs) + else: + raise nose.plugins.skip.SkipTest( + 'Extension to handle "%s" files was not available at compile time' + % extension + ) - return wrapper + return wrapper - return test_wrapper + return test_wrapper diff --git a/bob/io/image.py b/bob/io/image.py index 832225afd44bdce7f81516575fcdfb5ee881b703..6776e3c4d64f0e9077744def7d27c4a6111d3fa2 100644 --- a/bob/io/image.py +++ b/bob/io/image.py @@ -1,4 +1,5 @@ import numpy as np + from PIL import Image diff --git a/buildout.cfg b/buildout.cfg index 7d772471c6cf238ea79f3053f215f2b13e0e50d1..0fa626f9f74dcd900ca604a1a430cf7d8143415d 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -10,4 +10,4 @@ newest = false verbose = true [scripts] -recipe = bob.buildout:scripts \ No newline at end of file +recipe = bob.buildout:scripts diff --git a/doc/conf.py b/doc/conf.py index ea466ee8a3329c275287ed6e90ca6f16c4599670..5d4e765a85492283d2f65eb950e4cbf1eb95f2d7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -2,32 +2,29 @@ # vim: set fileencoding=utf-8 : import os -import sys -import glob -import pkg_resources +import pkg_resources # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.3' +needs_sphinx = "1.3" # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig', - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.doctest', - 'sphinx.ext.graphviz', - 'sphinx.ext.intersphinx', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', - 'sphinx.ext.mathjax', - #'matplotlib.sphinxext.plot_directive' - ] + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.ifconfig", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.graphviz", + "sphinx.ext.intersphinx", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinx.ext.mathjax", +] # Be picky about warnings nitpicky = False @@ -36,13 +33,13 @@ nitpicky = False nitpick_ignore = [] # Allows the user to override warnings from a separate file -if os.path.exists('nitpick-exceptions.txt'): - for line in open('nitpick-exceptions.txt'): +if os.path.exists("nitpick-exceptions.txt"): + for line in open("nitpick-exceptions.txt"): if line.strip() == "" or line.startswith("#"): continue dtype, target = line.split(None, 1) target = target.strip() - try: # python 2.x + try: # python 2.x target = unicode(target) except NameError: pass @@ -58,25 +55,27 @@ autosummary_generate = True numfig = True # If we are on OSX, the 'dvipng' path maybe different -dvipng_osx = '/opt/local/libexec/texlive/binaries/dvipng' -if os.path.exists(dvipng_osx): pngmath_dvipng = dvipng_osx +dvipng_osx = "/opt/local/libexec/texlive/binaries/dvipng" +if os.path.exists(dvipng_osx): + pngmath_dvipng = dvipng_osx # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'bob.io.base' +project = "bob.io.base" import time -copyright = u'%s, Idiap Research Institute' % time.strftime('%Y') + +copyright = "%s, Idiap Research Institute" % time.strftime("%Y") # Grab the setup entry distribution = pkg_resources.require(project)[0] @@ -92,42 +91,42 @@ release = distribution.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['links.rst'] +exclude_patterns = ["links.rst"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # Some variables which are useful for generated material -project_variable = project.replace('.', '_') -short_description = u'Basic IO for Bob' -owner = [u'Idiap Research Institute'] +project_variable = project.replace(".", "_") +short_description = "Basic IO for Bob" +owner = ["Idiap Research Institute"] # -- Options for HTML output --------------------------------------------------- @@ -135,80 +134,81 @@ owner = [u'Idiap Research Institute'] # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. import sphinx_rtd_theme -html_theme = 'sphinx_rtd_theme' + +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = project_variable +# html_short_title = project_variable # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'img/logo.png' +html_logo = "img/logo.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = 'img/favicon.ico' +html_favicon = "img/favicon.ico" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a <link> tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = project_variable + u'_doc' +htmlhelp_basename = project_variable + "_doc" # -- Post configuration -------------------------------------------------------- @@ -218,26 +218,27 @@ rst_epilog = """ .. |project| replace:: Bob .. |version| replace:: %s .. |current-year| date:: %%Y -""" % (version,) +""" % ( + version, +) # Default processing flags for sphinx -autoclass_content = 'class' -autodoc_member_order = 'bysource' +autoclass_content = "class" +autodoc_member_order = "bysource" autodoc_default_options = { - "members": True, - "undoc-members": True, - "show-inheritance": True, + "members": True, + "undoc-members": True, + "show-inheritance": True, } # For inter-documentation mapping: from bob.extension.utils import link_documentation, load_requirements + sphinx_requirements = "extra-intersphinx.txt" if os.path.exists(sphinx_requirements): - intersphinx_mapping = link_documentation( - additional_packages=['python','numpy'] + \ - load_requirements(sphinx_requirements) - ) + intersphinx_mapping = link_documentation( + additional_packages=["python", "numpy"] + + load_requirements(sphinx_requirements) + ) else: - intersphinx_mapping = link_documentation() - - + intersphinx_mapping = link_documentation() diff --git a/doc/extra-intersphinx.txt b/doc/extra-intersphinx.txt index 9c61c73639974afe3eafae36df0f4c8f36a72206..9a635b910d93ff2d9b1c24b7bc7ce15600b65d31 100644 --- a/doc/extra-intersphinx.txt +++ b/doc/extra-intersphinx.txt @@ -1 +1 @@ -scipy \ No newline at end of file +scipy diff --git a/doc/index.rst b/doc/index.rst index 74ade76611a9b7954f0f7e8fdfd6a44817f9f477..3e0e720755ca5b5348233bb5c84dd632de20b758 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -13,7 +13,7 @@ Below is the old documentation. This module contains a basic interface to read and write files of various types. It provides generic functions :py:func:`bob.io.base.save` and -:py:func:`bob.io.base.load` to write and read various types of data. +:py:func:`bob.io.base.load` to write and read various types of data. To enable further types of IO, please import one of the following packages (the list might not be exhaustive): @@ -25,7 +25,7 @@ Documentation .. toctree:: :maxdepth: 2 - py_api + py_api TODO ---- diff --git a/doc/py_api.rst b/doc/py_api.rst index bec56c499f404a46c25d909bdad0e191c5d57959..2373795e8d98312e3ea84f9af57c83d680d101cd 100644 --- a/doc/py_api.rst +++ b/doc/py_api.rst @@ -16,6 +16,3 @@ Functions bob.io.base.save bob.io.base.create_directories_safe bob.io.base.vstack_features - - - diff --git a/pyproject.toml b/pyproject.toml index bb5e83cb40b5871b321beb790ae14606b010e4ca..b738dc847ff9705c5769673db7415f2eb9a75f4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,12 @@ [build-system] -requires = ["setuptools", "wheel", "bob.extension"] -build-backend = "setuptools.build_meta" + requires = ["setuptools", "wheel", "bob.extension"] + build-backend = "setuptools.build_meta" + +[tool.isort] + profile = "black" + line_length = 80 + order_by_type = true + lines_between_types = 1 + +[tool.black] + line-length = 80 diff --git a/requirements.txt b/requirements.txt index 33a37b06361e73d0284f0d31668e365593104f9d..32506f993451e5f98c4342c48604b630ef0e59d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ bob.extension h5py imageio -numpy \ No newline at end of file +numpy diff --git a/setup.py b/setup.py index 99c943d9a5f182e1ec0025264f3fb9ceb9fa9444..b435850c14ce55c1e5a9e83ebfe17b8dc7825bc2 100644 --- a/setup.py +++ b/setup.py @@ -3,11 +3,11 @@ # Andre Anjos <andre.anjos@idiap.ch> # Mon 16 Apr 08:18:08 2012 CEST # -from setuptools import setup, dist +from setuptools import dist, setup dist.Distribution(dict(setup_requires=["bob.extension"])) -from bob.extension.utils import load_requirements, find_packages +from bob.extension.utils import find_packages, load_requirements install_requires = load_requirements()