Version 0.14.3
0.14.3 (13 April 2021) - Testing and all that jazz edition:
New:
unpythonic.test.fixtures
, a lightweight testing framework for macro-enabled Python code.- Context managers
session
,testset
, andcatch_signals
. Various helper functions, such asreturns_normally
(for use in atest[]
). - Testing macros, similar to the builtin
assert
, but with the magic of conditions and restarts: even if a test fails or errors out, further tests continue running.test[expr]
,test[expr, message]
,test_raises[exctype, expr]
,test_raises[exctype, expr, message]
,test_signals[exctype, expr]
,test_signals[exctype, expr, message]
.- To help diagnose test failures with minimum fuss, the
test[...]
macro provides an optional markerthe[expr]
to capture the values of interesting subexpressions inside atest[...]
, for display in the test failure message (along with the corresponding source code).- Often even that is not needed; by default, if no
the[]
are present,test[]
captures the value of the leftmost term when the test is a comparison (common use case).
- Often even that is not needed; by default, if no
- Helper macros
fail[message]
,error[message]
andwarn[message]
for producing unconditional failures, errors or warnings.
- Context managers
callsite_filename
: return the filename from which this function is being called. Useful as a building block for debug utilities and similar.equip_with_traceback
: take a manually created exception instance, equip it with a traceback. Requires Python 3.7 or later.subset
: test whether an iterable is a subset of another. Convenience function.allsame
: test whether all elements of an iterable are the same. Sometimes useful in writing testing code.safeissubclass
: like issubclass, but ifcls
is not a class, swallow theTypeError
and returnFalse
. Sometimes useful when dealing with lots of code that needs to check types dynamically.
Non-breaking changes:
s
now has a convenience mode for generating cyclic infinite sequences.m
is nowimathify
andmg
is nowgmathify
, for descriptiveness, and for consistency with naming other abstractions inunpythonic
. The old names will remain working in v0.14.x, and will be removed in v0.15.0.@generic
and@typed
can now decorate instance methods, class methods and static methods. This makes those methods (OOP sense) have methods (generic function sense). Get it?self
andcls
parameters do not participate in dispatching, and need no type annotation.- Beside appearing as the first positional-or-keyword parameter, the self-like parameter must be named one of
self
,this
,cls
, orklass
to be detected by the ignore mechanism. This limitation is due to implementation reasons; while a class body is being evaluated, the context needed to distinguish a method (OOP sense) from a regular function is not yet present. - OOP inheritance support: when
@generic
is installed on an OOP method (instance method, or@classmethod
), then at call time, classes are tried in MRO order. All generic-function methods of the OOP method defined in the class currently being looked up are tested for matches first, before moving on to the next class in the MRO. (This has subtle consequences, related to in which class in the hierarchy the various generic-function methods for a particular OOP method are defined.) - To work with OOP inheritance,
@generic
must be the outermost decorator (except@classmethod
or@staticmethod
, which are essentially compiler annotations). - However, when installed on a
@staticmethod
, the@generic
decorator does not support MRO lookup, because that would make no sense. See discussions on interaction between@staticmethod
andsuper
in Python: [1] [2].
- To ease installation, relax version requirement of the optional MacroPy dependency to the latest released on PyPI, 1.1.0b2.
- Once MacroPy updates, we'll upgrade; 1.1.0b2 is missing some small features we would like to use (particularly the
.transform
attribute of macros, which allows calling the underlying syntax transformer function).
- Once MacroPy updates, we'll upgrade; 1.1.0b2 is missing some small features we would like to use (particularly the
- Conditions: when an unhandled
error
orcerror
occurs, the original unhandled error is now available in the__cause__
attribute of theControlError
exception that is raised in this situation. - Conditions: on Python 3.7+,
signal
now equips the condition instance with a traceback, for consistency withraise
. - Document named-arg bug in
curry
in the docstring. See #61. Fixing this needs a betterpartial
, so for now it's a known issue. - All of
unpythonic
itself is now tested using the new testing framework for macro-enabled code,unpythonic.test.fixtures
. Hence, developingunpythonic
now requires MacroPy. For usingunpythonic
, MacroPy remains strictly optional, as it will at least for the foreseeable future.
Breaking changes:
- Experimental:
@generic
no longer takes a master definition. Methods (in the generic function sense) are registered directly with@generic
; the first method definition implicitly creates the generic function.
Fixed:
- Compatibility with Pythons 3.4, 3.5 and 3.7, thanks to a newly set up CI workflow for automated multi-version testing. Also test coverage (statement coverage) is measured by the workflow.
- Significantly improved test coverage, from 85% to 92%. See #68. Many small bugs fixed.
- PyPy3 support: fixed crash in querying the arity of builtin functions. See #67.
- Condition system:
with handlers
catches also derived types, e.g. a handler forException
now catches a signaledValueError
.signal(SomeExceptionClass)
now implicitly creates an instance with no arguments, just likeraise
does.- Conditions can now inherit from
BaseException
, not only fromException.
mogrify
now skipsnil
, actually making it useful for processingll
linked lists. Although this is technically a breaking change, the original behavior was broken, so it should not affect any existing code.