Skip to content

pymetta 0.9.3

Choose a tag to compare

@MesTTo MesTTo released this 25 Sep 19:12

pymetta 0.9.3 and all seventeen integration packages at 0.9.3, each pinning pymetta==0.9.3. tsmetta 0.0.1-alpha.2 and cmetta 2.0.0 are released beside it on the same engine.

Before you upgrade

A SWI-Prolog you built yourself has to be rebuilt. A pymetta host now needs 37 patches to SWI-Prolog where 0.9.2's needed 19: the engine requires 35 to swipl-devel itself and pymetta the 2 to janus, and each refuses at boot a host that lacks one, naming each missing patch. On Linux x86_64 under CPython 3.12, 3.13 and 3.14 the wheel carries the patched host, so pip install pymetta is still the whole install; anywhere else, rebuild and redeclare the host as docs/patched-host.md describes. pymetta also checks its two janus patches right after boot and refuses a home that lacks one with EngineError.

The licence is Apache-2.0: pymetta and its seventeen members declared MIT through 0.9.2. The wheel carries LICENSE and NOTICE, and NOTICE keeps the MIT copyright and permission notice of PeTTa, the engine's origin.

These change what an existing program sees:

  • Hook verdicts, typing-rule outcomes and translator-rule refusals are capitalized constructors. A pre-add or post-add handler answers (Accept), (Accept <atom>), (Refuse <words>) or (Drop); an add-typing-rule! outcome is Accept, (Refuse <reason>) or Defer. The lowercase spellings are refused by name. In Python the builders are Accept, Refuse and Drop, replacing accept, refuse and drop.
  • The Python seat's evaluation options fuel and unmatched are gone. Every evaluation runs in the evaluation fuel scope, so (pragma! max-stack-depth N) bounds an eval as it bounds a ! form, and answers an unmatched call by the dispatch policy.
  • The py-* doors refuse with the platform refusal naming the python capability wherever no Python seat is loaded, under tsmetta and in an engine booted without the extensions token, where each call used to answer itself unreduced.
  • A registration of Prolog that its contract refuses raises RegistrationError, which is also a ValueError, and carries requires, what the registration lacks. register_prolog(path=...) of a file that is not there raises SourceNotFound.
  • lib_process, lib_socket and lib_http import on a build without their capability, and only the call that needs it refuses, with PlatformCapabilityError.
  • setup! names its subject by the rule get-property and from follow: a bare name is a library under the library root and a relative path is written ./x.
  • An open get-atoms read of a native space meets the atoms shortest first, in insertion order within one length. MeTTa specifies no order across a space's atoms; a program that relied on the old order, SWI's procedure-table hash order, sees a different one.
  • A |-> lambda is named by its content, lambda_ and 16 hex digits, instead of a process-wide counter, so a saved space names its lambdas the same in every process.
  • The engine no longer publishes metta_typed_dispatch_applies/2.

New

  • Every seat evaluates through one engine door, metta_host_evaluate/5, which runs a term in the fuel scope and answers each value as data with its well-founded residue.
  • Registering Prolog as MeTTa functions is one engine service, metta_register_prolog/3, which PyMeTTa's register_prolog, tsmetta's registerProlog and cmetta's mt_register_prolog all cross once, so the three refuse the same names before a source loads.
  • Space.copy() restores a space's rows into the copy the way a load restores a program and compiles in the copy exactly what the source had compiled (metta_host_copy_rows/2).
  • The library pack's page, lib/README.md, shows a worked example of every one of the 61 libraries again, each quoted from the corpus program that exercises it and held to it by the library-readme lane.

Fixed

  • A Prolog source loaded on another thread, or inside an engine of its own, raises the errors it prints, where a syntax error in a source registered from a worker thread was lost.
  • A term stored through a Python space provider comes back as the term the engine gave it, so a stored partial application applies as it does in a native space.
  • A specialization that fails in one space no longer stops the same call specializing in another, writing a function into one space no longer compiles other spaces' waiting equations of that name, and importing a library no longer fails when an earlier import's function has been specialized in the same space.
  • A library head backed by Prolog stays a function in every space still importing the library when the first space that imported it is dropped.
  • (get-property Subject Key) answers or refuses by name for every subject a program can write, and a library name of .. is refused as leaving the library root.
  • unicode-normalize's nfkc-casefold removes default-ignorable code points, as Unicode's NFKC_Casefold does.
  • lib_http's server answers a request whose Accept header carries a wildcard, as curl, browsers and Python's requests send.
  • @define compiles a function that reuses a finished for loop's variable as a later loop's counter.
  • LimitSize in lib_nars and lib_pln answers at every size.
  • Two threads reading one platform capability for the first time no longer read an absent capability as present.
  • No callback the garbage collector runs for the Python seat takes a lock, a watch() iterator collected without close() no longer cancels its subscription from inside the collector, and an abandoned Channel releases its SWI message queue.
  • CITATION.cff and the README's citation block name the release, where both read 0.8.0 through 0.9.2; a test now holds them, and the changelog's newest heading, to the version the package builds.

The full list, with the development tooling's changes, is the 0.9.3 section of the changelog.

Known issues

Each of these three is fixed after this release.

  • A long-lived process that creates and releases many contexts and imports libraries into them grows the engine's predicate registry with its history, so a later import costs far more than the same import in a fresh process. One test run replayed in a single process held 7.7 million predicates, 1.55 million clauses and 4.4 GB by its 5,255th test, and a property test creating a fresh MeTTa() and importing lib.file for each example took about 130 s and added about 1.37 million predicates, against 4.8 s and under 500 in a fresh process. What amplifies the growth is still being identified. Each import's shadow repair also sweeps every import receipt the process holds, which costs time but does not add the predicates; a repair that reaches only the import's own names follows this release.
  • A race pulled through a streaming door, such as race() or a fn call, is counted partly inside that door's held engine, and that part varies with the schedule, so its m.stats() block is not one integer: 200 races through Space.fn read 1,038 to 9,049 inferences, where the same race through run reads 1,167 every time. The fix takes a held goal's work through the engine's own accounting, so a stopped branch's discarded work stays out of the block.
  • Cancelling a spawned future costs the caller 27 inferences more when the task is still queued than when a carrier is already running it, and the schedule decides which, so two equal blocks can differ by 27. The fix charges every branch of the cancel alike.

How this release was checked

These are the files built from the release commit and checked before upload. Every distribution builds an sdist and a pure wheel from a tree holding only committed state, and all of them pass twine check --strict. Each of pymetta's three manylinux wheels passes check_host_bundle.py and boots from a clean virtual environment outside the repository under env -i, answering from its own host. The pure wheel beside PyPI's janus-swi on a stock SWI-Prolog is refused with EngineError. All seventeen members install beside pymetta pinning pymetta==0.9.3 and their entry points load. Every published entry point resolves with each extra it declares: 495 of 495 combinations over Linux x86_64 and aarch64, macOS arm64 and x86_64, and Windows amd64, for CPython 3.12, 3.13 and 3.14.