Posts

Reload a fixture module after changing it

importlib.reload() re-executes a module that was imported successfully, using its original loader. This example creates a temporary directory containing fixture_reload.py , adds that directory to sys.path , and imports the fixture. Its first concrete value is "one" . The source is then replaced with a longer assignment whose value is "two changed" ; invalidate_caches() asks import finders to discard relevant discovery caches before reload() is called. Assertions check the first value, that reload returned the same module object in this case, and the newly defined value. The output contains only the final value, so it does not depend on the temporary directory name. The finally block removes this fixture's path entry and module-cache entry even if an assertion fails. Changing the file length also makes this small fixture less dependent on a filesystem timestamp with coarse resolution. Reloading is not a general reset mechanism. Python retains the module dic...

Preserve key case in a ConfigParser

By default, ConfigParser converts option names to lowercase. That is convenient for case-insensitive configuration, but it changes spelling such as APIKey when a parser reads and later writes an INI document. To preserve option-name case, set optionxform to str before reading or setting options. str returns each incoming option name unchanged, which is the idempotent canonicalization behavior the parser expects. This example parses a [headers] section containing APIKey . The key list remains APIKey , retrieval using that exact spelling returns demo-token , and retrieval with lowercase apikey fails. The caught NoOptionError confirms the direct consequence of choosing case preservation: option lookup becomes case-sensitive. Section names have separate behavior and are case-sensitive by default regardless of this setting. Configure optionxform before ingesting configuration, because names already read with the default transformer have already been lowercased. Case preservation ...

List package resource names without assuming filesystem paths

importlib.resources.files() returns a Traversable resource container, not a promise that a package is installed as an ordinary directory. That distinction matters because packages can be imported from mechanisms such as zip files. To list resource names, call files(package).iterdir() and work with each returned object's resource-oriented methods, including name and is_file() . This example creates a temporary package containing alpha.txt and beta.txt , imports it, and asks its resource container for direct children. The comprehension selects only text-file resources and sorts their names, so its output is deterministic: alpha.txt, beta.txt . The assertion checks the expected names for this controlled fixture. The code deliberately does not convert a resource to pathlib.Path , call __file__ , or construct a path relative to an installed package. Those approaches can couple an application to one installation layout. iterdir() is non-recursive, so nested resources require expl...

Use a subcommand to select a dry-run action

argparse subcommands make the selected operation visible in the command shape, instead of making a dry-run behavior depend only on a boolean flag. This example creates a dry-run subparser with one positional target . Its set_defaults() call stores a callable as action , so parsing the synthetic arguments dry-run cache produces a namespace containing the subcommand name, target, and selected action. The action returns a preview string; it does not delete a cache or access the filesystem. The assertions verify that parsing selected dry-run , that the callable produced the expected preview, and that the output is exactly would-delete cache . This keeps parsing and dispatch close together while making the dry-run path explicitly testable. add_subparsers() creates the command group. Passing required=True means parsing without a subcommand is an error; that option requires Python 3.7 or later. set_defaults() can attach arbitrary values to the parsed namespace, but it does not ensure...

Extract numbered issue references without matching prefixes

Issue text can contain a useful reference such as #12 alongside strings where a number sign is part of a larger token. The pattern (?<![A-Za-z0-9_])#(\d+) uses a one-character negative lookbehind before # . It therefore extracts 12 and 7 , but rejects pre#34 and ABC_#56 : in each rejected case, the number sign has an ASCII letter or underscore immediately before it. The capturing group contains only the digits, which is why findall() returns digit strings rather than complete #12 text. The assertion fixes the intended behavior for this input, and the list printout has stable ordering because findall() reports non-overlapping matches from left to right. Python's standard re module supports fixed-width negative lookbehinds; this one is exactly one character wide. Read the official re.Pattern.findall() documentation , the description of negative lookbehind , and the special sequence reference . These APIs are available in current supported Python 3 versions. “Prefix” is ...

Use INI interpolation for a derived local path

INI interpolation can express a derived local path without repeating its base component. ConfigParser uses basic interpolation by default, so %(root)s/cache refers to the root option in the same section. The example reads a paths section where root is the relative local directory local-data ; retrieving cache expands the reference to local-data/cache . The assertions deliberately inspect both forms. get(..., raw=True) returns the stored template, while an ordinary get() returns the interpolated value. This distinction matters for diagnostics and for tools that need to preserve a template rather than consume its resolved setting. Interpolation is resolved when the value is retrieved, so referenced options do not need to appear earlier in the INI text. A derived string is not automatically a usable filesystem location. This example does not create a directory, normalize separators, reject traversal components, or confirm that the path exists. It only demonstrates configuration ...

Inspect a module origin using find_spec

importlib.util.find_spec() can expose a module's ModuleSpec without binding that module name in the current namespace. A spec includes an origin field: for source modules it is commonly a filename, while loaders without a file location can use another meaningful marker. This example looks up the standard-library sys module. In CPython, its expected origin is the exact string "built-in" , so the code asserts that marker and prints the fully qualified spec name plus a deterministic Boolean result. The concrete input is therefore "sys" , not a pathname. The output does not disclose an installation directory and avoids treating origin as a universally usable filesystem path. That distinction matters: a module spec's origin describes the loader's loading location, and namespace packages may have None instead. Furthermore, module.__file__ and module.__spec__.origin are not kept synchronized if either changes at runtime. Inspect a returned spec for d...