Repository navigation
Escape string arguments as Python string literals - #16
Merged
Merged
Conversation
This was referenced Oct 8, 2026
ru551n
force-pushed
the
escape-string-args
branch
from
October 14, 2026 09:20
d346e30 to
fa73b4c
Compare
LarsAsplund
reviewed
Oct 15, 2026
| ## Changes | ||
|
|
||
| - `import_module_from_file` resolves a relative path against the directory of the testbench file, like `exec_file`, rather than against the working directory of the simulator. It must be called after `test_runner_setup`. Callers that relied on a path relative to the working directory must pass an absolute path instead. See #7. | ||
| - `arg(value : string)` and `kwarg(kw, value : string)` now produce a valid Python string literal: quotes, backslashes and control characters in the text are escaped. Callers that escaped the text themselves (for example passing `\\n` to get a newline) now get that text escaped again, and must pass the plain text instead. |
Contributor
There was a problem hiding this comment.
Make it a news fragment
Collaborator
Author
There was a problem hiding this comment.
Claude (AI assistant) here, on behalf of @ru551n:
Fixed in a482910. The note is now release_notes/news.d/16.breaking.md. The repository had no news fragments yet, so the commit also:
- configures towncrier (
[tool.towncrier]in pyproject.toml, types breaking/feature/bugfix/doc/misc), sotowncrier build --version <version>collects the fragments intorelease_notes/unreleased.mdwhen releasing; - shows the fragments under Unreleased in the docs;
- makes
tools/release.py validate --tagfail while fragments are left.
arg and kwarg for string now escape quotes, backslashes and control characters, so any text round-trips exactly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The release notes of the next release are now news fragments in release_notes/news.d, which towncrier collects into release_notes/unreleased.md when releasing. The documentation shows them under Unreleased, and the release check fails while any are left. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ru551n
force-pushed
the
escape-string-args
branch
from
October 15, 2026 07:35
fa73b4c to
a482910
Compare
The draft of the news fragments is rendered by towncrier itself, through the towncrier-draft-entries directive, instead of by code in conf.py. The release notes page is generated as Markdown, since the directive parses the Markdown towncrier writes like the page holding it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ru551n
added a commit
to ru551n/vunit-python-bridge
that referenced
this pull request
Oct 15, 2026
- counter_vc and filter_vc read p_model of their handle, like the user guide and VUnit's own verification components, instead of a get_model accessor. - The Python objects page no longer repeats that string arguments are passed verbatim, a semantic of python_pkg that VUnit#16 changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ru551n
added a commit
to ru551n/vunit-python-bridge
that referenced
this pull request
Oct 16, 2026
- counter_vc and filter_vc read p_model of their handle, like the user guide and VUnit's own verification components, instead of a get_model accessor. - The Python objects page no longer repeats that string arguments are passed verbatim, a semantic of python_pkg that VUnit#16 changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
ru551n
added a commit
that referenced
this pull request
Oct 21, 2026
* Support Riviera-PRO through the bridge with VHPI Riviera-PRO has no direct binding (VHPIDIRECT), and was served by a separate VHPI application with a subset of the API. It now calls the bridge like the other simulators, through a VHPI front end, native/vhpi.c, that registers a foreign subprogram per entry point of bridge.h. The front end is compiled into a variant of the library, like the FLI one for Questa, against the vhpi_user.h of the simulator, and the generated attributes name the library by absolute path without its suffix. Strings cross as vectors of character positions, since Riviera-PRO ends a string value at a line feed. The VUnit interface of Riviera-PRO has no run environment hook, so the setup sets VUNIT_PYTHON_BRIDGE_CONFIG in the environment of the VUnit process, which vsim inherits. The VHPI application (python_pkg_vhpi.vhd, native/vhpi/, foreign_application.py) and the expected failures of the tests on VHPI are removed. Two test names lose their brackets, which VUnit's Tcl scripts for Riviera-PRO evaluate as commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address review: Riviera-PRO only, explicit front ends Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the release note a news fragment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Prototype Python objects owned by VHDL python_object_t is an instance of a Python class living in a session of its own, created on first use and called with call(object, "method", args). Every VC instance can get an object of its own without namespace or path handling, and a bench can create one and pass it to a VC through a generic. Hand-written prototype for review of the API; the full set of result types would be generated once it is agreed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Generate the call and eval overloads of python_object_t python_object_pkg.vhd is generated from python_object_pkg.vhd.in by generate_python_pkg.py, from the same result tables as python_pkg, so an object has every result type of call and eval, the procedure results and the same aliases. A test checks that both generated packages are up to date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say that python_object_pkg.vhd is generated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Rename object_or_new to get Like get_logger and get_id, it gives the object it is given or a new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document Python objects and report objects that cannot be created An object whose class cannot be imported or constructed reports that error once and is then bound to a stand-in raising the reason on every use, rather than leaving self undefined. The user guide gets a Python objects section, and the README and the release notes mention them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name the constructor new_python_object and enumerate anonymous objects new_object is now new_python_object and get is get_python_object, as VUnit names the constructors and getters of its types. An object without an id is python_bridge:python:object:<n>, enumerated like VUnit's verification components. The user guide makes the handle constructor of a VUnit style verification component the place to create its object, which tests/counter_vc_pkg.vhd now shows; the handle-less component taking its instance path is tests/path_counter_vc.vhd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add exec and get_logger for objects and report objects on VHPI exec(object, code) executes code with the object as self, and get_logger(object) is the logger reporting its errors. On Riviera-PRO/Active-HDL, which have no sessions, the first use of an object says that objects require NVC, GHDL or Questa. The user guide notes that an integer_array_t cannot be a constructor argument of an object made during elaboration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show Python objects as backends in the embedded_python example filter_vc is a verification component whose handle, made by new_filter_vc, holds the Python object that is its behaviour. The new test case drives three instances: two with objects of their own and one given an object of the testbench, which the testbench queries too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show how a model class is made importable, fix the class example From the verification component pattern of #14: the model of a verification component as a package next to it, put on PYTHONPATH by the run script or installed with pip install -e. The class example of the independent models section gets its file from the model_file generic of the component instead of runner_cfg, which a component does not have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Polish python_object_pkg Name the failures of objects after what failed: new_python_object("<class>") for a failed creation and call("<method>") for the procedure form of a method call, rather than an exec of their session. p_create and p_self are private to the package body, and the PROTOTYPE marker is gone. The user guide shows a component calling its object in a process and how errors are named. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cover create and get_session of objects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Lay out the instantiations like standard ones One association per line in the generic and port maps of the tests, the example and the user guide, the layout speja gives them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address the review of Python objects - A class is named by a plain dotted name, resolved from the longest prefix that is a module, so nested classes work without the module:Class form. - __vunit__.create is __vunit__.instantiate_as_self, after what it does. - p_supports_sessions is gone: the FFI of Riviera-PRO/Active-HDL already reports the first use of a session other than the default one. - filter_vc of the example takes a clock and a valid input. - The user guide gets a "Where Python code is found" chapter on the run script directory, the Python environment, PYTHONPATH, file names and global module names, and clearer paragraphs on backends of verification components and shared objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say plainly when Python code is not found without setup The Where Python code is found chapter gets a subsection per topic, and the verification component example states that its folder is not importable until the run script adds it to PYTHONPATH, before showing how. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Simplify python_object_pkg and the runtime - pydoc.locate resolves dotted class names instead of a hand-written loop. - The identity check is in VHDL only, which also catches a duplicate at elaboration; the Python check of the session was a second copy of it. - Runtime.namespace is the property itself rather than a wrapper of it. - create is the procedure the generated code calls, without p_create behind it, p_self is inlined and the operation of a failed creation is stored rather than rebuilt from the class name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Find classes of objects more clearly and report a failed object on every use A class name now starts in the default session, so a class reached through a name defined there, like "np.ndarray", is found, and is otherwise imported with importlib rather than pydoc, which is no longer imported at every simulation start. A name that is not found, or not a class, is reported in one line rather than with the traceback of the bridge. The stand-in of an object that could not be created also fails on assignments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fail early on objects that cannot work and name the call in its errors - new_python_object reports, on the logger of the identity, an identity already used by an object or by a session made with new_session, and a simulator without sessions (Riviera-PRO/Active-HDL), and then returns null_python_object rather than carrying on. Using null_python_object is reported on python_logger. - The VHPI p_exec stub reports and executes nothing, rather than running the code in the default session. - Every form of call on an object reports its errors as call("<method>"), like the procedure did: the call overloads evaluate with p_eval and the result getters, which the FFI packages now all have. - create is named create_now, which is what it does. - Tests of the error paths: a used identity, an object that cannot be created, the naming of call errors, separate sessions and numbering, exec and get_logger before creation, null_python_object, the eval procedures and the identities of create_std_cfg. - The generated lines are at most 120 characters, and the docstring of the generator describes both packages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Merge where Python code is found into File paths and update the objects The section on how modules are found is now the "Python modules" part of "File paths", whose table already covered file names, with one recipe for PYTHONPATH. The objects chapter describes the new identity rule, the failure on Riviera-PRO/Active-HDL, the class lookup and the error names of call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Apply the learnings of trying objects in a real project Converting a stream-width model kept in module globals to an object per source, in a project that used exec_file models before, showed: - A misspelled class in a module that does import said only "No class named", which reads like a missing module. It now names the module and the missing attribute. - The user guide gets a recipe for moving from a model with module state to an object, says that the id is optional, and recommends create_now to report wrong constructor arguments at the start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pair new_python_object with create_python_object new_python_object makes an object that Python constructs on first use; create_python_object makes one that is constructed by the call, so a wrong class or constructor argument is reported there. They replace new_python_object with create_now, and the step that constructs an object on first use is private. create_python_object only works in a process: NVC cannot find the foreign functions of the bridge during elaboration ("foreign function vpy_buffer_clear not found") and GHDL cannot call them then. The user guide has a table of when to use each, and errors name the one called. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give Python objects a page of their own in the documentation The Python objects chapter moves from Running Python code to its own page, after Sessions, and python_object_pkg gets a page in the VHDL API reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address review: drop the simulator check of objects, new docs example Remove p_sessions_supported: on Riviera-PRO/Active-HDL, the first use of an object already fails in the VHPI primitives, and whether Riviera-PRO is supported is a question of its own. The shared object example of the docs is a memory model instead of a scoreboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Get Python objects by identity or full name get_python_object(id) and get_python_object(name) return the object made with that identity, like get_logger, but report an identity without an object as a failure instead of making one. new_python_object and create_python_object take the identity by its full name as well. The get_python_object giving a component a default backend is replaced by an if in the constructor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bind a Python object to obj instead of self in its session self is also the first parameter of the methods of classes defined in the session, which made code mixing the two hard to read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name a Python object made without an id after its class Objects without an id are enumerated per class, the dotted class name giving the identity hierarchy under python_bridge:python:object, e.g. python_bridge:python:object:models:counter_model:Counter:1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Return null_python_object for an identity without an object get_python_object no longer reports a failure for an identity without an object, so a testbench can check whether the object is made and make it otherwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give verification components a constructor per use case A component making its own object and a component given one have constructors of their own instead of an optional model parameter. The user guide describes the identities of components and their objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Give path_counter_vc a handle instead of several generics The handle holds the step and the id. Without an id the component still takes its instance path as the identity, in its process, and the testbench reaches the object by that path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the named eval and call forms, adding the alias call_integer The tests and the user guide use eval_integer, call_real and the like rather than qualified expressions. The call returning integer, call_integer_w_arg, gets the alias call_integer in python_pkg and python_object_pkg, named like the other results. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add function forms of the vector results sized like to_unsigned eval and call of std_ulogic_vector, signed and unsigned results, in python_pkg and for objects, are also functions taking the width as a length or as size_res. They call the procedure forms, so an error returns a vector of 'U'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Leave the result of a failed sized function out of the docs A failure stops the test before the function returns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Align the docs and comments of Python objects with the latest changes - The python_pkg comment and the example no longer say that signed and unsigned results are procedures only; the example shows the sized function too. - The aliasing of call is described for the functions without a width. - The new features of the release notes are listed under New features. - One class name in the identity examples of the user guide, and a class name given to a constructor rather than as a generic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Use the model of a handle directly and drop a python_pkg caveat - counter_vc and filter_vc read p_model of their handle, like the user guide and VUnit's own verification components, instead of a get_model accessor. - The Python objects page no longer repeats that string arguments are passed verbatim, a semantic of python_pkg that #16 changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make the release notes news fragments Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Shorten the news fragment of Python objects, drop the call_integer one The details are in the user guide, and the call_integer alias is nothing a user needs to know about. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Name objects without an id python_object:<class>:<n> The default identity was python_bridge:python:object:package:module:Class:<n>. python_object:package.module.Class:<n> is shorter and as unique: the counter separates the objects of a class and the dotted class name the classes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
arg(value : string)andkwarg(kw, value : string)wrapped the text in double quotes verbatim, so a"or\(or a line break) in the text produced invalid or wrong Python.Change
Both now build the literal with
p_quoted, which escapes",\, LF and CR, and now also TAB and all other control characters (code < 32 and DEL) as\xNN. Bytes >= 128 are kept as they are so UTF-8 text is decoded by Python as the same characters.python_pkg.vhdis regenerated from the template. The user guide is updated, and the release note is the news fragmentrelease_notes/news.d/16.breaking.md. News fragments are new: towncrier collects them intorelease_notes/unreleased.mdwhen releasing, and the docs show them under Unreleased.Behaviour change
Callers that escaped the text themselves (for example passing
\nto get a newline) now get that text escaped again, and must pass the plain text instead.Tests
tb_python_pkg_bridge: round trip of quote, backslash, single quote, LF, TAB, CR, NUL, DEL and non-ASCII througharg/kwarg, plus theto_call_stroutput.tb_python_pkg(also run on VHPI simulators): length/ord checks of escaped strings.tests/run.py, the embedded_python example andpytest tests/test_python_bridge.pypass on NVC and GHDL.🤖 Generated with Claude Code