Skip to content

Commit

Permalink
Documentation build-time dependencies.
Browse files Browse the repository at this point in the history
This commit significantly improves our "setuptools"-based installer with
respect to documentation build-time dependencies. Specifically, two new
extras are now defined, installable as follows:

* "pip install -e .[dev]", installing "beartype" in editable mode as
  well as all dependencies required to both locally test "beartype"
  *and* build documentation for "beartype" from the command line.
* "pip install beartype[doc-rtd]", installing "beartype" as well as all
  dependencies required to build documentation from the external
  third-party Read The Docs (RTD) host.

In theory, this commit should resolve long-standing RTD build failures.
(*Prismatic optimism opts out of ontologically outre diasporas!*)
  • Loading branch information
leycec committed Feb 3, 2021
1 parent 4687983 commit e9ab557
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 244 deletions.
14 changes: 2 additions & 12 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,11 @@ python:
install:
- method: pip
path: .

#FIXME: Actually define a new "doc-rtd" extra in "setup.py", please. To
#do so, let's convert the existing "doc/source/requirement.txt" file into
#our ad-hoc project-specific format to enable developers to manually
#mimic this exact RTD configuration. To facilitate this:
#* Define a new "beartype.meta.LIBS_DOCTIME_MANDATORY" tuple resembling:
# LIBS_DOCTIME_MANDATORY = {
# 'sphinx >=3.4.3',
# 'sphinx-rtd-theme >=0.5.1',
# }
#* Define a new 'extras_require' key-value pair in setup resembling:
# 'doc': meta.LIBS_DOCTIME_MANDATORY,
extra_requirements:
- doc-rtd

# Permit Read The Docs (RTD) to reuse Python packages installed by its
# system package manager, which mostly means the standard scientific stack.
system_packages: true

# ....................{ SPHINX }....................
Expand Down
397 changes: 200 additions & 197 deletions README.rst

Large diffs are not rendered by default.

30 changes: 18 additions & 12 deletions beartype/_decor/_code/_pep/_error/peperror.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@
This private submodule is *not* intended for importation by downstream callers.
'''

#FIXME: *WOOPS.* We tragically realize we need to significantly refactor this
#entire package for O(1) behaviour, because... I mean, really. Consider large
#nested lists, yes? That's all we need to say. Specifically:
#
#* Refactor all private submodules of this package as follows:
# * If the "random_int" parameter is non-"None", specifically type-check
# *ONLY* the sole index of the current container indicated by that
# parameter.
# * Else, continue to perform O(n)-style type-checking as we currently do.
#FIXME: Generalizing the "random_int" concept (i.e., the optional "random_int"
#parameter accepted by the raise_pep_call_exception() function) that enables
#O(1) rather than O(n) exception handling to containers that do *NOT* provide
Expand All @@ -31,17 +22,32 @@
# by the pep_code_check_hint() function that uniquely store the value of each
# item, key, or value returned by each access of a non-indexable container
# iterator into a new unique local variable. Note this unavoidably requires:
# * Adding a new index to the ".hint_curr_meta" tuples internally created by
# that function -- named, say, "_HINT_META_INDEX_ITERATOR_VALUE". The value
# * Adding a new index to the "hint_curr_meta" tuples internally created by
# that function -- named, say, "_HINT_META_INDEX_ITERATOR_NAME". The value
# of the tuple item at this index should either be:
# * If the currently iterated type hint is a non-indexable container, the
# name of the new unique local variable assigned to by this assignment
# expression whose value is obtained from the iterator cached for that
# container.
# * Else, "None".
# Actually... hmm. Perhaps we only need a new local variable
# "iterator_nonsequence_names" whose value is a cached "FixedList" of
# sufficiently large size (so, "SIZE_BIG"?). We could then simply
# iteratively insert the names of the wrapper-specific new unique local
# variables into this list.
# Actually... *WAIT.* Is all we need a single counter initialized to, say:
# iterators_nonsequence_len = 0
# We then both use that counter to:
# * Uniquify the names of these wrapper-specific new unique local variables
# during iteration over type hints.
# * Trivially generate a code snippet passing a list of these names to the
# "iterators_nonsequence" parameter of raise_pep_call_exception() function
# after iteration over type hints.
# Right. That looks like The Way, doesn't it? This would seem to be quite a
# bit easier than we'd initially thought, which is always nice. Oi!
# * Python >= 3.8, but that's largely fine. Python 3.6 and 3.7 are
# increasingly obsolete in 2021.
#* Add a new optional "iterator_values" parameter to the
#* Add a new optional "iterators_nonsequence" parameter to the
# raise_pep_call_exception() function, accepting either:
# * If the current parameter or return of the parent wrapper function was
# annotated with one or more non-indexable container type hints, a *LIST* of
Expand Down
42 changes: 42 additions & 0 deletions beartype/_util/cache/utilcachecall.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,48 @@ class and returns a boolean. Under conservative assumptions of 4 bytes of
# Avoid circular import dependencies.
from beartype._util.utilobject import SENTINEL

#FIXME: *UGH.* The stdlib "inspect" module is horrifyingly inefficient.
#Calling *ANY* functions of that module will be strictly prohibited at some
#point, so we'd might as well start now. Admittedly, this inefficiency is
#only paid at decoration time, but we call this decorator *A LOT*
#throughout the codebase. Instead, replace this call to the
#inspect.signature() function with iteration manually iterating over
#function arguments and deciding whether any such arguments are variadic.
#
#This is sufficiently general-purpose logic, however, that we should:
#* Define a new "beartype._util.func.utilfuncargs" submodule.
#* In that submodule:
# * Define a new is_func_args_variadic() tester returning True if and only
# if one or more arguments of the passed callable are either positional
# or keyword variadic.
# * Exercise this tester with unit tests.
#
#To implement this function, note that the
#inspect._signature_from_function() implements the core of the "inspect"
#module's callable introspection. Given this implementation, testing for
#variadicity is actually surprisingly trivial. Something resembling the
#following should suffice:
#
#def is_func_args_variadic(func: 'Callable') -> bool:
#
# # Code object underlying the passed callable if any *OR* "None"
# # otherwise.
# func_code = getattr(func, '__code__', None)
#
# # If *NO* code object underlies this callable, raise an exception.
# if func_code is None:
# raise ...
#
# # We can't believe it's this simple, either. But it is.
# return (
# (func_code.co_flags & CO_VARARGS) or
# (func_code.co_flags & CO_VARKEYWORDS)
# )
#
#*YIKES.* That turns out to be a friggin' O(1) operation! This is why you
#never trust someone else's public API, people -- excluding ours, of
#course. Ours is awesome.

# Signature of the decorated callable.
func_sig = inspect.signature(func)

Expand Down
Empty file added beartype/_util/func/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions beartype/_util/utilcallable.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
This private submodule is *not* intended for importation by downstream callers.
'''

#FIXME: Rename this entire submodule to the new "beartype._util.func.utilfunc".
#Once we start dynamically inspecting callables ourselves rather than relying
#on the "inspect" module, we're going to have a plethora of these sorts of
#submodules lying around. Huzzah!

# ....................{ IMPORTS }....................
# from collections.abc import Callable

Expand Down
118 changes: 105 additions & 13 deletions beartype/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ def _convert_version_str_to_tuple(version_str: str) -> tuple:
# ....................{ METADATA ~ libs }....................
LIBS_RUNTIME_OPTIONAL = ()
'''
Optional runtime dependencies for this package defined as a tuple of
:mod:`setuptools`-specific requirements strings of the format
``{project_name} {comparison1}{version1},...,{comparisonN}{versionN}``, where:
Optional runtime package dependencies as a tuple of :mod:`setuptools`-specific
requirements strings of the format ``{project_name}
{comparison1}{version1},...,{comparisonN}{versionN}``, where:
* ``{project_name}`` is a :mod:`setuptools`-specific project name (e.g.,
``numpy``, ``scipy``).
Expand All @@ -281,27 +281,119 @@ def _convert_version_str_to_tuple(version_str: str) -> tuple:
'''


#FIXME: Consider defining this as well:
# LIBS_TESTTIME_OPTIONAL = (
# 'checkdocs',
# )
#That would probably be used in a new "[dev]" extra.
LIBS_TESTTIME_MANDATORY = (
# ....................{ METADATA ~ libs }....................
LIBS_TESTTIME_MANDATORY_TOX = (
# A relatively modern version of pytest is required.
'pytest >=4.0.0',
)
'''
**Mandatory tox test-time package dependencies** (i.e., dependencies required
to test this package under :mod:`tox`) as a tuple of :mod:`setuptools`-specific
requirements strings of the format ``{project_name}
{comparison1}{version1},...,{comparisonN}{versionN}``.
See Also
----------
:data:`LIBS_RUNTIME_OPTIONAL`
Further details.
'''


LIBS_TESTTIME_MANDATORY = LIBS_TESTTIME_MANDATORY_TOX + (
# pytest should ideally remain the only hard dependency for testing on
# local machines. While our testing regime optionally leverages third-party
# frameworks and pytest plugins (e.g., "tox", "pytest-xdist"), these
# dependencies are *NOT* required for simple testing.
#
# A relatively modern version of py.test is required.
'pytest >=4.0.0',
# A relatively modern version of tox is required.
'tox >=3.20.1',
)
'''
**Mandatory developer test-time package dependencies** (i.e., dependencies
required to test this package with :mod:`tox` as a developer at the command
line) as a tuple of :mod:`setuptools`-specific requirements strings of the
format ``{project_name} {comparison1}{version1},...,{comparisonN}{versionN}``.
See Also
----------
:data:`LIBS_RUNTIME_OPTIONAL`
Further details.
'''

# ....................{ METADATA ~ libs : doc }....................
_LIB_VERSION_MIN_SPHINX = '3.4.3'
'''
Human-readable minimum version as a ``.``-delimited string of :mod:`sphinx`
required to build package documentation.
'''


_LIB_VERSION_MIN_SPHINX_RTD_THEME = '0.5.1'
'''
Human-readable minimum version as a ``.``-delimited string of the **Read The
Docs (RTD)-flavoured Sphinx theme** (i.e., :mod:`sphinx_rtd_theme`) optionally
leveraged when building package documentation.
'''


LIBS_DOCTIME_MANDATORY = (
f'sphinx >={_LIB_VERSION_MIN_SPHINX}',
)
'''
**Mandatory developer documentation build-time package dependencies** (i.e.,
dependencies required to manually build documentation for this package as a
developer at the command line) as a tuple of :mod:`setuptools`-specific
requirements strings of the format ``{project_name}
{comparison1}{version1},...,{comparisonN}{versionN}``.
For flexibility, these dependencies are loosely relaxed to enable developers to
build with *any* versions satisfying at least the bare minimum. For the same
reason, optional documentation build-time package dependencies are omitted.
Since our documentation build system emits a non-fatal warning for each missing
optional dependency, omitting these optional dependencies here imposes no undue
hardships while improving usability.
See Also
----------
:data:`LIBS_RUNTIME_OPTIONAL`
Further details.
'''


LIBS_DOCTIME_MANDATORY_RTD = (
f'sphinx =={_LIB_VERSION_MIN_SPHINX}',
f'sphinx-rtd-theme =={_LIB_VERSION_MIN_SPHINX_RTD_THEME}',
)
'''
Mandatory test-time dependencies for this package defined as a tuple of
:mod:`setuptools`-specific requirements strings of the format
**Mandatory Read The Docs (RTD) documentation build-time package dependencies**
(i.e., dependencies required to automatically build documentation for this
package from the third-party RTD hosting service) as a tuple of
:mod:`setuptools`-specific requirements strings of the format ``{project_name}
{comparison1}{version1},...,{comparisonN}{versionN}``.
For consistency, these dependencies are strictly constrained to force RTD to
build against a single well-tested configuration known to work reliably.
See Also
----------
:data:`LIBS_RUNTIME_OPTIONAL`
Further details.
'''

# ....................{ METADATA ~ libs : dev }....................
LIBS_DEVELOPER_MANDATORY = LIBS_TESTTIME_MANDATORY + LIBS_DOCTIME_MANDATORY
'''
**Mandatory developer package dependencies** (i.e., dependencies required to
develop and meaningfully contribute pull requests for this package) as a tuple
of :mod:`setuptools`-specific requirements strings of the format
``{project_name} {comparison1}{version1},...,{comparisonN}{versionN}``.
This tuple includes all mandatory test- and documentation build-time package
dependencies and is thus a convenient shorthand for those lower-level tuples.
See Also
----------
:data:`LIBS_RUNTIME_OPTIONAL`
Further details.
'''

2 changes: 0 additions & 2 deletions doc/requirements.txt

This file was deleted.

23 changes: 17 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,26 @@ def _sanitize_classifiers(
# All optional runtime dependencies.
'all': meta.LIBS_RUNTIME_OPTIONAL,

# All mandatory testing dependencies, copied from the "tests_require"
# key below into an arbitrarily named extra. This is required *ONLY*
# for integration with the top-level "tox.ini" file. See the "extras"
# key in that file for further details.
'test': meta.LIBS_TESTTIME_MANDATORY,
# All mandatory developer dependencies (including all mandatory test-
# and documentation build-time dependencies) as referenced from
# external project documentation for developers.
'dev': meta.LIBS_DEVELOPER_MANDATORY,

# All mandatory Read The Docs (RTD)-specific documentation build-time
# dependencies an arbitrarily named extra. This is required *ONLY*
# for integration with the top-level ".readthedocs.yml" file. See the
# "python" key in that file for further details.
'doc-rtd': meta.LIBS_DOCTIME_MANDATORY_RTD,

# All mandatory tox-specific testing dependencies, copied from the
# "tests_require" key below into an arbitrarily named extra. This is
# required *ONLY* for integration with the top-level "tox.ini" file.
# See the "extras" key in that file for further details.
'test-tox': meta.LIBS_TESTTIME_MANDATORY_TOX,
},

# Mandatory testing dependencies.
'tests_require': meta.LIBS_TESTTIME_MANDATORY,
'tests_require': meta.LIBS_TESTTIME_MANDATORY_TOX,

# ..................{ PACKAGES }..................
# List of the fully-qualified names of all Python packages (i.e.,
Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ extras =
# https://stackoverflow.com/questions/29870629/pip-install-test-dependencies-for-tox-from-setup-py
# https://stackoverflow.com/questions/39922650/tox-tests-use-setup-py-extra-require-as-tox-deps-source
# https://github.com/tox-dev/tox/issues/13#issuecomment-247788280
# Note that this also requires ".[test]" to be listed as a dependency.
test
# Note that this also requires ".[test-tox]" to be listed as a dependency.
test-tox

# Comma- and newline-delimited string listing the names of all mandatory
# dependencies when testing this project.
Expand Down

0 comments on commit e9ab557

Please sign in to comment.