Skip to content

Commit

Permalink
Python Development Mode (PDM).
Browse files Browse the repository at this point in the history
This commit enables the Python Development Mode (PDM) by default under
both pytest and tox and thus continuous integration (CI), mildly
improving the robustness of our test suite in edge cases that absolutely
should *never* apply (e.g., GIL and memory safety violations) but
probably will nonetheless.

## Tests Improved

* **Python Development Mode (PDM).** The PDM (e.g., `-X dev`,
  `PYTHONDEVMODE`) is now enabled by default under both pytest and tox
  and thus continuous integration (CI), mildly improving the robustness
  of our test suite in edge cases that absolutely should *never* apply
  (e.g., GIL and memory safety violations) but probably will, because
  bad things always happen to good coders. It's, like, a law.

(*Lawless outlaws brigaded by ligand-bound brigands!*)
  • Loading branch information
leycec committed Mar 6, 2021
1 parent 99225ee commit 3d618d1
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 11 deletions.
39 changes: 38 additions & 1 deletion beartype/_decor/_code/_pep/_pephint.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@
'''

# ....................{ TODO }....................
#FIXME: Add support for Python 3.10 and thus:
#* PEP 604-compliance (e.g., "def square(number: int | float): pass"). Note
# PEP 604 thankfully preserves backward compatibility with "typing.Union":
# The existing typing.Union and | syntax should be equivalent.
# int | str == typing.Union[int, str]
# This means that we should:
# * Require no changes to the "beartype" package to support PEP 604.
# * Add unit tests explicitly support PEP 604 compliance under Python >= 3.10
# to the "beartype_test" package.
# * Note this support in documentation.
#* PEP 612-compliance. Since we don't currently support callable annotations,
# we probably can't extend that non-existent support to PEP 612. Nonetheless,
# we *ABSOLUTELY* should ensure that we do *NOT* raise exceptions when passed
# the two new "typing" singletons introduced by this:
# * "typing.ParamSpec", documented at:
# https://docs.python.org/3.10/library/typing.html#typing.ParamSpec
# * "typing.Concatenate", documented at:
# https://docs.python.org/3.10/library/typing.html#typing.Concatenate
# Ideally, we should simply ignore these singletons for now in a similar
# manner to how we currently ignore type variables. After all, these
# singletons are actually a new unique category of callable-specific type
# variables. See also:
# https://www.python.org/dev/peps/pep-0612

#FIXME: Byte string values aren't displayed properly in messages: e.g.,
# ...as value "b"What? Haven't you ever seen a byte-string separator
# before?"" not str.
Expand Down Expand Up @@ -852,6 +876,13 @@
# applies to an edge case under obsolete Python versions, so... *shrug*
#* Else, a non-fatal warning should be emitted and the portion of that type
# hint that *CANNOT* be safely checked under Python <= 3.7 should be ignored.
#FIXME: Note that mapping views now provide a "mapping" attribute enabling
#direct access of the mapping mapped by that view under Python >= 3.10:
# The views returned by dict.keys(), dict.values() and dict.items() now all
# have a mapping attribute that gives a types.MappingProxyType object
# wrapping the original dictionary.
#This means that we do *NOT* need to explicitly cache the "mapping" object
#mapped by any cached view under Python >= 3.10, reducing space consumption.

#FIXME: *WOOPS.* The "LRUCacheStrong" class is absolutely awesome and we'll
#absolutely be reusing that for various supplementary purposes across the
Expand Down Expand Up @@ -2727,7 +2758,13 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# for which this function generates deeply type-checking code.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

# Switch on (as in, pretend Python provides a "switch" statement)
#FIXME: Python 3.10 provides proper syntactic support for "case"
#statements, which should allow us to dramatically optimize this
#"if" logic into equivalent "case" logic *AFTER* we drop support
#for Python 3.9. Of course, that will be basically never, so we'll
#have to preserve this for basically forever. What you gonna do?

# Switch on (as in, pretend Python provides a "case" statement)
# this attribute to decide which type of code to generate to
# type-check the current pith against the current hint.
#
Expand Down
10 changes: 5 additions & 5 deletions beartype/_util/py/utilpyversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
'''

# ....................{ IMPORTS }....................
import sys
from sys import version_info

# See the "beartype.cave" submodule for further commentary.
__all__ = ['STAR_IMPORTS_CONSIDERED_HARMFUL']

# ....................{ CONSTANTS ~ at least }....................
IS_PYTHON_AT_LEAST_4_0 = sys.version_info >= (4, 0)
IS_PYTHON_AT_LEAST_4_0 = version_info >= (4, 0)
'''
``True`` only if the active Python interpreter targets at least Python 4.0.0.
'''
Expand All @@ -25,7 +25,7 @@
#FIXME: After dropping Python 3.8 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
IS_PYTHON_AT_LEAST_3_9 = IS_PYTHON_AT_LEAST_4_0 or sys.version_info >= (3, 9)
IS_PYTHON_AT_LEAST_3_9 = IS_PYTHON_AT_LEAST_4_0 or version_info >= (3, 9)
'''
``True`` only if the active Python interpreter targets at least Python 3.8.0.
'''
Expand All @@ -34,7 +34,7 @@
#FIXME: After dropping Python 3.7 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
IS_PYTHON_AT_LEAST_3_8 = IS_PYTHON_AT_LEAST_3_9 or sys.version_info >= (3, 8)
IS_PYTHON_AT_LEAST_3_8 = IS_PYTHON_AT_LEAST_3_9 or version_info >= (3, 8)
'''
``True`` only if the active Python interpreter targets at least Python 3.8.0.
'''
Expand All @@ -43,7 +43,7 @@
#FIXME: After dropping Python 3.6 support:
#* Refactor all code conditionally testing this global to be unconditional.
#* Remove this global.
IS_PYTHON_AT_LEAST_3_7 = IS_PYTHON_AT_LEAST_3_8 or sys.version_info >= (3, 7)
IS_PYTHON_AT_LEAST_3_7 = IS_PYTHON_AT_LEAST_3_8 or version_info >= (3, 7)
'''
``True`` only if the active Python interpreter targets at least Python 3.7.0.
'''
Expand Down
4 changes: 4 additions & 0 deletions beartype_test/a00_unit/decor/code/test_codemain.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ def func_untyped(hint_param: hint_meta.hint) -> hint_meta.hint:
#pytest.warns() context manager appears to be broken on our
#local machine. We have no recourse but to unconditionally
#ignore this warning at the module level. So much rage!
#FIXME: It's likely this has something to do with the fact that
#Python filters deprecation warnings by default. This is almost
#certainly a pytest issue. Since this has become fairly
#unctuous, we should probably submit a pytest issue report.
# with pytest.warns(BeartypeDecorHintPepDeprecatedWarning):
# func_typed = beartype(func_untyped)
func_typed = beartype(func_untyped)
Expand Down
12 changes: 7 additions & 5 deletions pytest
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pushd "${script_dirname}" >/dev/null
# Run this project's pytest-based test suite with all passed arguments.
# Dismantled, this is:
#
# * "-X dev", enabling the Python Development Mode (PDM). See also commentary
# for the ${PYTHONDEVMODE} shell variable in the "tox.ini" file.
# * "--color=yes", unconditionally enable colour output to guarantee color
# under piped pagers (e.g., "less").
# * "--maxfail=1", halt testing on the first failure for interactive tests.
Expand Down Expand Up @@ -77,11 +79,11 @@ pushd "${script_dirname}" >/dev/null
# See the following official documentation for further details, entitled
# "Initialization: determining rootdir and inifile":
# https://docs.pytest.org/en/latest/customize.html
command python3 -m pytest --color=yes --maxfail=1 "${@}" .
# command python3.6 -m pytest --maxfail=1 "${@}" .
# command python3.8 -m pytest --maxfail=1 "${@}" .
# command python3.9 -m pytest --maxfail=1 "${@}" .
# command pypy3.6 -m pytest --maxfail=1 "${@}" .
command python3 -X dev -m pytest --color=yes --maxfail=1 "${@}" .
# command python3.6 -X dev -m pytest --maxfail=1 "${@}" .
# command python3.8 -X dev -m pytest --maxfail=1 "${@}" .
# command python3.9 -X dev -m pytest --maxfail=1 "${@}" .
# command pypy3.6 -X dev -m pytest --maxfail=1 "${@}" .

# 0-based exit code reported by the prior command.
exit_code=$?
Expand Down
24 changes: 24 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,30 @@ deps = .[test-tox]
# Newline-delimited string listing all environment variables to be temporarily
# set in each shell subprocess running tests.
setenv =
# Enable the Python Development Mode (PDM), which:
# "Introduces additional runtime checks that are too expensive to be
# enabled by default. It should not be more verbose than the default if
# the code is correct; new warnings are only emitted when an issue is
# detected."
# Specifically, the PDM enables:
# * "-W default", emitting warnings ignored by default. Yes, Python
# insanely ignores various categories of warnings by default -- including
# deprecating warnings, which *ABSOLUTELY* should be emitted by default,
# but aren't. We can't resolve that for end users but we can resolve that
# for ourselves.
# * "PYTHONMALLOC=debug", registering memory allocators hooks detecting
# unsafe call stack, memory, and GIL violations.
# * "PYTHONFAULTHANDLER=1", registering fault handlers emitting Python
# tracebacks on signal faults.
# * "PYTHONASYNCIODEBUG=1", enabling asyncio debug mode logging unawaited
# coroutines.
# * Detections for unsafe string encoding and decoding operations.
# * Logging io.IOBase.close() exceptions on object finalization.
# * Enabling the "dev_mode" attribute of "sys.flags".
# See also:
# https://docs.python.org/3/library/devmode.html
PYTHONDEVMODE = 1

# Prevent Python from buffering and hence failing to log output in the
# unlikely (but feasible) event of catastrophic failure from either the
# active Python process or OS kernel.
Expand Down

0 comments on commit 3d618d1

Please sign in to comment.