Skip to content

Version 0.14.3

Compare
Choose a tag to compare
@Technologicat Technologicat released this 13 Apr 15:07
· 927 commits to master since this release

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, and catch_signals. Various helper functions, such as returns_normally (for use in a test[]).
    • 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 marker the[expr] to capture the values of interesting subexpressions inside a test[...], 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).
      • Helper macros fail[message], error[message] and warn[message] for producing unconditional failures, errors or warnings.
  • 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 if cls is not a class, swallow the TypeError and return False. 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 now imathify and mg is now gmathify, for descriptiveness, and for consistency with naming other abstractions in unpythonic. 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 and cls 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, or klass 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 and super 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).
  • Conditions: when an unhandled error or cerror occurs, the original unhandled error is now available in the __cause__ attribute of the ControlError exception that is raised in this situation.
  • Conditions: on Python 3.7+, signal now equips the condition instance with a traceback, for consistency with raise.
  • Document named-arg bug in curry in the docstring. See #61. Fixing this needs a better partial, 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, developing unpythonic now requires MacroPy. For using unpythonic, 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 for Exception now catches a signaled ValueError.
    • signal(SomeExceptionClass) now implicitly creates an instance with no arguments, just like raise does.
    • Conditions can now inherit from BaseException, not only from Exception.
  • mogrify now skips nil, actually making it useful for processing ll linked lists. Although this is technically a breaking change, the original behavior was broken, so it should not affect any existing code.