Releases: Technologicat/unpythonic
v0.15.3
0.15.3 (27 September 2024) - New tree snakes edition:
IMPORTANT:
- Minimum Python language version is now 3.8.
- We support 3.8, 3.9, 3.10, 3.11, 3.12, and PyPy3 (language versions 3.8, 3.9, and 3.10).
- Python 3.6 and 3.7 support dropped, as these language versions have officially reached end-of-life. If you need
unpythonic
for Python 3.6 or 3.7, use version 0.15.2.
- Minimum version for optional macro expander
mcpyrate
is now 3.6.2, because theastcompat
utility module was moved there.
New:
- Python 3.12 support.
- As in, all tests pass, so there are no regressions. Some undiscovered interactions with new language features (
type
statement) may still be broken, although the most obvious cases are already implemented.
- As in, all tests pass, so there are no regressions. Some undiscovered interactions with new language features (
- Python 3.11 support.
- As in, all tests pass, so there are no regressions. Some undiscovered interactions with new language features (
try
/except*
construct) may still be broken, although the most obvious cases are already implemented.
- As in, all tests pass, so there are no regressions. Some undiscovered interactions with new language features (
- Walrus syntax
name := value
is now supported, and preferred, for all env-assignments. Old syntaxname << value
still works, and will remain working at least until v0.16.0, whenever that is.- Note that language support for using an assignment expression inside a subscript without parenthesizing it was added in Python 3.10.
- If you still use Python 3.8 or 3.9, with the new
:=
syntax you must put parentheses around eachlet
binding, because syntactically, the bindings subform looks like a subscript. - All documentation is written in Python 3.10 syntax; all unit tests are written in Python 3.8 syntax.
Changed:
- Utility module
unpythonic.syntax.astcompat
, used by the macro layer, moved tomcpyrate.astcompat
. This module handles version differences in theast
module in various versions of Python.
Fixed:
ETAEstimator
edge case: at any point after all tasks have been marked completed, return a constant zero estimate for the remaining time.- Fix borkage in
mathseq
when running with SymPy 1.13 (SymPy is only used in tests). Bump SymPy version to 1.13. - Fix bug in scopeanalyzer:
get_names_in_store_context
now collects also names bound inmatch
/case
constructs (pattern matching, Python 3.10).
v0.15.2
0.15.2 (19 September 2024)
This time, just a small but important fix.
Fixed:
unpythonic.env.env
is now pickleable. Save your fancy bunches into.pickle
files and load them back!
Future plans:
Contrary to appearances, this project is not dead. But it already does most of what I personally need it to do, so it is pretty much in maintenance mode. And it has not required much maintenance over the past two years.
We still plan to officially support Python 3.11+ later, as well as to update all constructs with assignment semantics to use the more appropriate :=
operator, when/if I find the time to do so. The syntax uses <<
for historical reasons - these constructs were originally implemented in 2018, on Python 3.4, back when :=
did not exist.
The most likely upgrade timeframe is when I personally switch to Python 3.11+, and something breaks. That is also when I'll likely next upgrade the sister project mcpyrate
.
Version 0.15.1
0.15.1 (28 January 2022) - New Year's edition:
New:
- Python 3.10 support. Running on Python 3.10 requires
mcpyrate
3.6.0. - New module
unpythonic.timeutil
, with utilities for converting a number of seconds into human-understood formats (seconds_to_human
,format_human_time
), and a simple running-averageETAEstimator
that takes advantage of these. As usual, these are available at the top level ofunpythonic
. - Add function
unpythonic.syntax.get_cc
, the less antisocial little sister ofcall_cc
from an alternate timeline, to make programming with continuations slightly more convenient. (Alternate timelines happen a lot when one uses multi-shot continuations.) The two work together. See docstring. - Tag continuation closures (generated by the
with continuations
macro), for introspection.- To detect at run time whether a given object is a continuation function, use the function
unpythonic.syntax.iscontinuation
. - This is purely an introspection feature;
unpythonic
itself does not use this information. For why you might want to query this, seeget_cc
, particularly the examples in unit tests. - The information is stored as an attribute on the function object; keep this in mind if you intend to wrap the continuation function with another function. (Strictly, this is the correct behavior, since a custom wrapper is not a continuation function generated by the
with continuations
macro.)
- To detect at run time whether a given object is a continuation function, use the function
Fixed:
- The test framework
unpythonic.test.fixtures
is now correctly installed when installingunpythonic
. See #81. - The subpackage for live REPL functionality,
unpythonic.net
, is now correctly installed when installingunpythonic
. - Fix a broken import that prevented the REPL server
unpythonic.net.server
from starting. This was broken by the move ofasync_raise
intounpythonic.excutil
in 0.15.0. unpythonic.syntax.prefix
: Fix wrong macro name in error message ofunpythonic.syntax.prefix.u
. Document in the docstring that the magic operatorsq
,u
, andkw
(of theprefix
macro) cannot be renamed by as-importing.- Preserve the source location info of the dialect-import statement in the example dialects in
unpythonic.dialects
. In the output, the lines of expanded source code that originate in a particular dialect template are marked as coming from the unexpanded source line that contains the corresponding dialect-import.- If you want to see the line numbers before and after dialect expansion, use the
StepExpansion
dialect frommcpyrate.debug
. - This fix requires
mcpyrate
3.6.0 or later. The code will run also on earlier versions ofmcpyrate
; then, just like before, it will look as if all lines that originate in any dialect template came from the beginning of the user source code.
- If you want to see the line numbers before and after dialect expansion, use the
Version 0.15.0
0.15.0 (22 June 2021) - "We say 'howdy' around these parts" edition:
Beside introducing dialects (a.k.a. whole-module code transforms), this edition concentrates on upgrading our dependencies, namely the macro expander, and the Python language itself, to ensure unpythonic
keeps working for the next few years. This introduces some breaking changes, so we have also taken the opportunity to apply any such that were previously scheduled.
We have sneaked in some upgrades for other subsystems, too. Particularly curry
, the multiple dispatch system (@generic
), and the integration between these two have been improved significantly.
IMPORTANT:
- Minimum Python language version is now 3.6.
- We support 3.6, 3.7, 3.8, 3.9 and PyPy3 (language versions 3.6 and 3.7).
- For future plans, see our Python language version support status.
- The optional macro expander is now
mcpyrate
.
If you still need unpythonic
for Python 3.4 or 3.5, use version 0.14.3, which is the final version of unpythonic
that supports those language versions.
The same applies if you need the macro parts of unpythonic
(i.e. import anything from unpythonic.syntax
) in your own project that uses MacroPy. Version 0.14.3 of unpythonic
works up to Python 3.7.
New:
-
Dialects! New module
unpythonic.dialects
, providing some example dialects that demonstrate what can be done with a dialects system (i.e. full-module code transformer) together with a kitchen-sink language extension macro package such asunpythonic
. -
Improved robustness: several auxiliary syntactic constructs now detect at macro expansion time if they appear outside any valid lexical context, and raise
SyntaxError
(with a descriptive message) if so.- The full list is:
call_cc[]
, forwith continuations
it
, foraif[]
local[]
/delete[]
, fordo[]
q
/u
/kw
, forwith prefix
where
, forlet[body, where(k0=v0, ...)]
(also forletseq
,letrec
,let_syntax
,abbrev
)with expr
/with block
, forwith let_syntax
/with abbrev
- Previously these constructs could only raise an error at run time, and not all of them could detect the error even then.
- The full list is:
-
Syntactic consistency: allow env-assignment notation and brackets to declare bindings in the
let
family of macros. The preferred syntaxes for thelet
macro are now:let[x << 42, y << 9001][...] # lispy expr let[[x << 42, y << 9001] in ...] # haskelly let-in let[..., where[x << 42, y << 9001]] # haskelly let-where
If there is just one binding, these become:
let[x << 42][...] let[[x << 42] in ...] let[..., where[x << 42]]
Similarly for
letseq
,letrec
, and the decorator versions; and for the expr forms oflet_syntax
,abbrev
. The reason for preferring this notation is that it is consistent with bothunpythonic
's env-assignments (let
bindings live in anenv
) and the use of brackets to denote macro invocations.To ease backwards compatibility, we still accept the syntax used up to v0.14.3, too.
Also, from symmetry and usability viewpoints, if a mix of brackets and parentheses are used, it hardly makes sense to require some specific mix - so this has been extended so that the choice of delimiter doesn't matter. All the following are also accepted, with the meaning exactly the same as above:
let[[x, 42], [y, 9001]][...] # best visual consistency let[(x, 42), (y, 9001)][...] let([x, 42], [y, 9001])[...] let((x, 42), (y, 9001))[...] # like up to v0.14.3 let[[[x, 42], [y, 9001]] in ...] # best visual consistency let[[(x, 42), (y, 9001)] in ...] let[([x, 42], [y, 9001]) in ...] let[((x, 42), (y, 9001)) in ...] # like up to v0.14.3 let[(x << 42, y << 9001) in ...] let[..., where[[x, 42], [y, 9001]]] # best visual consistency let[..., where[(x, 42), (y, 9001)]] let[..., where([x, 42], [y, 9001])] let[..., where((x, 42), (y, 9001))] # like up to v0.14.3 let[..., where(x << 42, y << 9001)]
For a single binding, these are also accepted:
let[x, 42][...] let(x, 42)[...] # like up to v0.14.3 let[[x, 42] in ...] let[(x, 42) in ...] # like up to v0.14.3 let[(x << 42) in ...] let[..., where[x, 42]] let[..., where(x, 42)] # like up to v0.14.3 let[..., where(x << 42)]
These alternate syntaxes will be supported at least as long as we accept parentheses to pass macro arguments; but in new code, please use the preferred syntaxes.
-
Miscellaneous.
with namedlambda
now understands the walrus operator, too. In the constructf := lambda ...: ...
, the lambda will get the namef
. (Python 3.8 and later.)with namedlambda
now auto-names lambdas that don't have a name candidate using their source location info, if present. This makes it easy to see in a stack trace where some particular lambda was defined.- Multiple-dispatch system
unpythonic.dispatch
:- Use consistent terminology:
- The function that supports multiple call signatures is a generic function.
- Its individual implementations are multimethods.
- Add decorator
@augment
: add a multimethod to a generic function defined elsewhere. - Add function
isgeneric
to detect whether a callable has been declared@generic
. - Add function
methods
: display a list of multimethods of a generic function. - It is now possible to dispatch on a homogeneous type of contents collected by a
**kwargs
parameter. curry
now supports@generic
functions. This feature is experimental. Semantics may still change.- The utilities
arities
,required_kwargs
, andoptional_kwargs
now support@generic
functions. This feature is experimental. Semantics may still change.
- Use consistent terminology:
curry
now errors out immediately on argument type mismatch.- Add
partial
, a type-checking wrapper forfunctools.partial
, that errors out immediately on argument type mismatch. - Add
unpythonic.excutil.reraise_in
(expr form),unpythonic.excutil.reraise
(block form): conveniently remap library exception types to application exception types. Idea from Alexis King (2016): Four months with Haskell. - Add variants of the above for the conditions-and-restarts system:
unpythonic.conditions.resignal_in
,unpythonic.conditions.resignal
. The new signal is sent using the same error-handling protocol as the original signal, so that e.g. anerror
remains anerror
even if re-signaling changes its type. - Add
resolve_bindings_partial
, useful for analyzing partial application. - Add
triangular
, to generate the triangular numbers (1, 3, 6, 10, ...). - Add
partition_int_triangular
to answer a timeless question concerning stackable plushies. - Add
partition_int_custom
to answer unanticipated similar questions. - All documentation files now have a quick navigation section to skip to another part of the docs. (For all except the README, it's at the top.)
- Python 3.8 and 3.9 support added.
Non-breaking changes:
-
Changes to how some macros expand.
-
Some macros, notably
letseq
,do0
, andlazyrec
, now expand into hygienic macro captures of other macros. Thecontinuations
macro also outputs a hygienically capturedaif
when transforming anor
expression that occurs in tail position.- This allows
mcpyrate.debug.step_expansion
to show the intermediate result, as well as brings the implementation closer to the natural explanation of how these macros are defined. (Zen of Python: if the implementation is easy to explain, it might be a good idea.) - The implicit do (extra bracket syntax) also expands as a hygienically captured
do
, but e.g. inlet[]
it will then expand immediately (due tolet
's inside-out expansion order) before control returns to the macro stepper. If you want to see the implicitdo[]
invocation, use the"detailed"
mode of the stepper, which shows individual macro invocations even when expanding inside-out:step_expansion["detailed"][...]
,with step_expansion["detailed"]:
.
- This allows
-
The
do[]
anddo0[]
macros now expand outside-in. The main differences from a user perspective are:- Any source code captures (such as those performed by
test[]
) show the expanded output ofdo
anddo0
, because that's what they receive. (For tests, you may want to use the macrowith expand_testing_macros_first
, which see.) mcpyrate.debug.step_expansion
is able to show the intermediate result after thedo
ordo0
has expanded, but before anything else has been done to the tree.
- Any source code captures (such as those performed by
-
-
Miscellaneous.
- Resolve issue #61:
curry
now supports kwargs properly.- We now analyze parameter bindings like Python itself does, so it should no longer matter whether arguments are passed by position or by name.
- Positional passthrough works as before. Named passthrough added.
- Any remaining arguments (that cannot be accepted by the initial call) are passed through to a callable intermediate result (if any), and then outward on the curry context stack as a
Values
. Sincecurry
in this role is essentially a function-composition utility, the receiving curried function instance unpacks theValues
into args and kwargs. - If any extra arguments (positional or named) remain w...
- Resolve issue #61:
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.
Version 0.14.2.1
0.14.2.1 just fixes that I forgot the include the final release date for 0.14.2 in CHANGELOG.md
. Real release below.
0.14.2 7 August 2020 - "Greenspun" edition:
With the arrival of conditions and restarts, and a REPL server, I think it is now fair to say unpythonic
contains an ad-hoc, informally-specified, slow implementation of half of Common Lisp. To avoid bug-ridden, we have tests - but it's not entirely impossible for some to have slipped through. If you find one, please file an issue in the tracker.
This release welcomes the first external contribution! Thanks to @aisha-w for the much improved organization and presentation of the documentation!
Language version:
We target Python 3.6. Now we test on both CPython and PyPy3.
Rumors of the demise of Python 3.4 support are exaggerated. While the testing of unpythonic
has moved to 3.6, there neither is nor will there be any active effort to intentionally drop 3.4 support until unpythonic
reaches 0.15.0.
That is, support for 3.4 will likely be dropped with the arrival of the next batch of breaking changes. The current plan is visible in the roadmap as the 0.15.0 milestone.
If you're still stuck on 3.4 and find something in the latest unpythonic
0.14.x doesn't work there, please file an issue. (Support for 0.14.x will end once 0.15 is released, but not before.)
New:
- Improve organization and presentation of documentation (#28).
- Macro README: Emacs syntax highlighting for
unpythonic.syntax
and MacroPy. - Resumable exceptions, a.k.a. conditions and restarts. One of the famous killer features of Common Lisp. Drawing inspiration from python-cl-conditions by Alexander Artemenko. See
with restarts
(Common Lisp equivalent:RESTART-CASE
),with handlers
(HANDLER-BIND
),signal
(SIGNAL
),invoke
(INVOKE-RESTART
). Many convenience forms are also exported; seeunpythonic.conditions
for a full list. For an introduction to conditions, see Chapter 19 in Practical Common Lisp by Peter Seibel. - REPL server and client. Interactively hot-patch your running Python program! Another of the famous killer features of Common Lisp. The server is daemonic, listening for connections in a background thread. (Don't worry, it's strictly opt-in.) See
unpythonic.net.server
andunpythonic.net.client
. - Batteries for network programming:
unpythonic.net.msg
: A simplistic message protocol for sending message data over a stream-based transport (such as TCP).unpythonic.net.ptyproxy
: Proxy between a Linux PTY and a network socket. Useful for serving terminal utilities over the network. This doesn't usepty.spawn
, so Python libraries that expect to run in a terminal are also welcome. Seeunpythonic.net.server
for a usage example.unpythonic.net.util
: Miscellaneous small utilities.
fix
: Break infinite recursion cycles (for pure functions). Drawing inspiration from original implementations by Matthew Might and Per Vognsen.- More batteries for itertools:
fixpoint
: Arithmetic fixed-point finder (not to be confused withfix
).within
: Yield items from iterable until successive iterates are close enough (useful with Cauchy sequences).chunked
: Split an iterable into constant-length chunks.lastn
: Yield the lastn
items from an iterable.pad
: Extend iterable to lengthn
with afillvalue
.interleave
: For example,interleave(['a', 'b', 'c'], ['+', '*']) --> ['a', '+', 'b', '*', 'c']
. Interleave items from several iterables, slightly differently fromzip
.find
: From an iterable, get the first item matching a given predicate. Convenience function.powerset
: Compute the power set (set of all subsets) of an iterable. Works also for infinite iterables.CountingIterator
: Count how many items have been yielded, as a side effect.slurp
: Extract all items from aqueue.Queue
(until it is empty) into a list, returning that list.map
: Curry-friendly thin wrapper for the builtinmap
, making it mandatory to specify at least one iterable.running_minmax
,minmax
: Extract both min and max in one pass over an iterable. Therunning_
variant is a scan and returns a generator; the just-give-me-the-final-result variant is a fold.
ulp
: Given a floatx
, return the value of the unit in the last place (the "least significant bit"). Atx = 1.0
, this is the machine epsilon, by definition of the machine epsilon.partition_int
: split a small positive integer, in all possible ways, into smaller integers that sum to it.dyn
now supports rebinding, using the assignment syntaxdyn.x = 42
. To mass-update atomically, seedyn.update
.box
now supports.set(newvalue)
to rebind (returns the new value as a convenience), andunbox(b)
to extract contents. Syntactic sugar for rebinding isb << newvalue
(whereb
is a box).ThreadLocalBox
: A box with thread-local contents. It also holds a default object, which is used when a particular thread has not placed any object into the box.Some
: An immutable box. Useful for optional fields; tell apart the presence of aNone
value (Some(None)
) from the absence of a value (None
).Shim
: A shim holds abox
or aThreadLocalBox
, and redirects attribute accesses to whatever object is currently in the box. The point is that the object in the box can be replaced with a different one later, while keeping the attribute proxy in place. One use case is to redirect standard output only in particular threads.islice
now supports negative start and stop. (Caution: no negative step; and it must consume the whole iterable to determine where it ends, if at all.)async_raise
: Inject KeyboardInterrupt into an arbitrary thread. (CPython only.)resolve_bindings
: Get the parameter bindings a given callable would establish if it was called with the given args and kwargs. This is mainly of interest for implementing memoizers, since this allows them to see (e.g.)f(1)
andf(a=1)
as the same thing fordef f(a): pass
.Singleton
: a base class for singletons that interacts properly withpickle
. The pattern is slightly pythonified; instead of silently returning the same instance, attempting to invoke the constructor while an instance already exists raisesTypeError
. This solution separates concerns better; see #22.sym
: a lispy symbol type; or in plain English: a lightweight, human-readable, process-wide unique marker, that can be quickly compared to another such marker by object identity (is
). These named symbols are interned. Supplying the same name to the constructor results in receiving the same object instance. Symbols survive apickle
roundtrip.gensym
: a utility to create a new, unique uninterned symbol. Like the pythonic idiomnonce = object()
, but with a human-readable label, and withpickle
support. Object identity of gensyms is determined by an UUID, generated when the symbol is created. Gensyms also survive apickle
roundtrip.
Experimental features:
Each experimental feature is a provisional proof-of-concept, usually lacking battle-testing and polish. Details may still change in a backwards-incompatible way, or the whole feature may still be removed. Do not depend on it in production!
- Multiple dispatch. The
generic
decorator makes a generic function with multiple dispatch. Arity and type annotations determine which method of the generic function a specific call of the function is dispatched to.- This essentially allows replacing the
if
/elif
dynamic type checking boilerplate of polymorphic functions with type annotations on the function parameters, with support for features from thetyping
stdlib module. - Inspired by the multi-methods of CLOS (the Common Lisp Object System), and the generic functions of Julia.
- This essentially allows replacing the
typed
: The little sister of thegeneric
decorator. Restrict allowed argument types to one specific combination only.isoftype
: The big sister ofisinstance
. Type check a value against a type specification at run time, with support for many (but not all) features from thetyping
module. This is the machinery that powers@generic
and@typed
.- If you need a run-time type checker for serious general use, consider the
typeguard
library.
- If you need a run-time type checker for serious general use, consider the
Non-breaking changes:
setescape
/escape
have been renamedcatch
/throw
, to match the standard terminology in the Lisp family. The old nonstandard names are now deprecated, and will be removed in 0.15.0.- The parameters of
raisef
are now more pythonic, just the objectexc
and an optional keyword-onlycause
. Old-style parameters are now deprecated, and will be removed in 0.15.0. See #30. runpipe
andgetvalue
are now both replaced by a single unified nameexitpipe
. This is just a...
Version 0.14.1
Retrofuturistic edition:
Language version:
- Support Python 3.6. First released in 2016, supported until 2021, most distros should have it by now.
- This will be the final release that supports Python 3.4; upstream support for 3.4 ended in March 2019.
New:
Popper
, a pop-while iterator.window
, a length-n sliding window iterator for general iterables.autoref[]
can now be nested.dbg[]
now supports also an expression variant, customizable by lexically assigningdbgprint_expr
. See the README on macros for details.
Bugfixes:
- Fix crash when SymPy or mpmath are not installed.
mogrify
is now part of the public API, as it should have been all along.- Docs: Mention the
mg
function in the README.
Non-breaking changes:
- Future-proof
namelambda
for Python 3.8. - Docs:
dbg[]
is now listed as a convenience feature in the README.
Version 0.14.0
"Dotting the t's and crossing the i's" edition:
Bugfixes:
setup.py
: macros are not zip safe, becauseZipImporter
fails to return source code for the module and MacroPy needs that.- fix splicing in the
do[]
macro;ExpandedDoView
should now work correctly - fix lambda handling in the
lazify
macro - fix
dict_items
handling inmogrify
(fixes the use of thecurry
macro with code usingfrozendict
)
New:
roview
: a read-only view into a sequence. Behaves mostly the same asview
, but has no__setitem__
orreverse
.mg
: a decorator to mathify a gfunc, so that it willm()
the generator instances it makes.- The
do[]
macro now supportsdelete[name]
to delete a local variable previously created in the same do-expression usinglocal[name << value]
. envify
block macro, to make formal parameters live in an unpythonicenv
.autoref
block macro, to implicitly reference attributes of an object (for reading only).
Breaking changes:
- The
macropy3
bootstrapper now takes the-m
option;macropy3 -m somemod
, likepython3 -m somemod
. The alternative is to specify a filename positionally;macropy3 somescript.py
, likepython3 somescript.py
. In either case, the bootstrapper will import the module in a special mode that pretends its__name__ == '__main__'
, to allow using the pythonic conditional main idiom also in macro-enabled code. - The constructor of the writable
view
now checks that the input is not read-only (roview
, or aSequence
that is not also aMutableSequence
) before allowing creation of the writable view. env
now checks finalization status also when deleting attrs (a finalizedenv
cannot add or delete bindings)
Non-breaking improvements:
Version 0.13.1
"Maybe a slice?" edition
New:
view
: writable, sliceable view into a sequence. Use likeview(lst)[::2]
. Can be nested (i.e. sliced again). Any access (read or write) goes through to the original underlying sequence. Can assign a scalar to a slice à la NumPy. Stores slices, not indices; works also if the length of the underlying sequence suddenly changes.islice
: slice syntax support foritertools.islice
, use likeislice(myiterable)[100:10:2]
orislice(myiterable)[42]
. (It's essentially a curried function, where the second step uses the subscript syntax instead of the function call syntax.)prod
: likesum
, but computes the product. A missing battery.iindex
: likelist.index
, but for iterables. A missing battery. (Makes sense mostly for memoized input.)inn(x, iterable)
: contains-check (x in iterable
) for monotonic infinite iterables, with automatic termination.getattrrec
,setattrrec
(recursive): access underlying data in an onion of wrappers.primes
andfibonacci
generators, mainly intended for testing and usage examples.SequenceView
andMutableSequenceView
abstract base classes;view
is aMutableSequenceView
.
Breaking changes:
- The
fup[]
utility macro to functionally update a sequence is gone and has been replaced by thefup
utility function, with slightly changed syntax to accommodate. New syntax is likefup(lst)[3:17:2] << values
. (This is a two-step curry utilizing the subscript and lshift operators.) ShadowedSequence
, and hence alsofupdate
, now raise the semantically more appropriateIndexError
(instead of the previousValueError
) if the replacement sequence is too short.namelambda
now returns a modified copy; the original function object is no longer mutated.
Non-breaking improvements:
ShadowedSequence
now supports slicing (read-only), equality comparison,str
andrepr
. Out-of-range access to a single item emits a meaningful error, like inlist
.env
anddyn
now provide thecollections.abc.Mapping
API.cons
and friends:BinaryTreeIterator
andJackOfAllTradesIterator
now support arbitarily deep cons structures.
Version 0.13.0
"I'll evaluate this later" edition:
New:
lazify
macro: call-by-need for Python (a.k.a. lazy functions, like in Haskell)frozendict
: an immutable dictionarymogrify
: in-placemap
for mutable containerstimer
: a context manager for performance testings
: create lazy mathematical sequences. For example,s(1, ...)
,s(1, 2, ...)
,s(1, 2, 4, ...)
ands(1, 2, ...)**2
are now valid Python. Regular function, no macros.m
: endow any iterable with infix math support. (But be aware that after that, applying an operation meant for general iterables drops the math support; to restore it,m(result)
again.)- The
unpythonic.llist
module now providesJackOfAllTradesIterator
that understands both trees and linked lists (with some compromises). nb
macro: a silly ultralight math notebook.
Breaking changes:
dyn
: theasdict
anditems
methods now return a live view.- The mutable single-item container
Box
and its data attributevalue
have been renamed tobox
andx
, respectively. namedlambda
macro: Env-assignments are now processed lexically, just like regular assignments. Added support for let-bindings.curry
macro: The special mode for uninspectables is now enabled lexically within thewith curry
block. Also, manual uses of thecurry
decorator (on bothdef
andlambda
) are now detected, and in such cases the macro now skips adding thecurry
decorator.
Non-breaking improvements:
namelambda
now supports renaming any function object, and also multiple times.- The single-item special binding syntax is now supported also by the bindings block of the
dlet
,dletseq
,dletrec
,blet
,bletseq
andbletrec
macros.