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...

Distinguish missing XML text from an empty string

Distinguish missing XML text from an empty string Element.text can be None , an empty string, or a non-empty string in an ElementTree object. Do not write if not element.text when the distinction matters: that condition groups None and "" together. First check whether find() returned an element, then compare its text value explicitly. The example builds three child elements in memory. missing-text has the default None text, empty-text is explicitly assigned "" , and message contains "enabled" ; asking for an absent child forms a fourth case. text_state() returns a separate label for each state, and assertions establish the intended classification before it is printed. There is an important parsing limitation. XML source such as <note></note> and <note/> contains no character data, and ElementTree normally represents both parsed elements with text is None . Consequently, ElementTree cannot recover an author’s lexical choice...

Require an INI section before reading settings

A configuration reader can make its required structure explicit before it asks for individual options. Here, read_service_mode() calls has_section("service") immediately after parsing. If the section is absent, it raises a clear ValueError ; otherwise it reads mode . The example exercises both paths: a valid document produces quiet , and a document containing only [logging] produces the predictable missing-section message. This check is useful when a section represents a required component rather than an optional collection of settings. It distinguishes a missing section from a missing option, so an application can present a focused diagnostic or choose its own fallback behavior. The subsequent get() can still fail if mode itself is absent, and this helper intentionally does not validate whether the returned mode is one of an application's allowed values. Add that validation separately when the setting controls program behavior. The parser's special DEFAULT v...

Write an INI configuration to StringIO

io.StringIO is a text stream kept in memory, which makes it useful when code needs an INI representation without opening a file. In this example, a ConfigParser receives a service section and two string options. Calling write() serializes the configuration into the StringIO destination, and getvalue() returns the complete generated text for comparison and output. The assertion includes every newline, including the blank line that ConfigParser.write() places after the section. That makes the output contract explicit: the printed text is exactly [service] , followed by the two options and a final blank line. The example also shows that values supplied through the mapping interface are strings, matching the INI parser's storage model. write() formats a configuration representation; it does not preserve arbitrary input formatting, comments, or every dialect-specific feature of an original file. It also does not validate that localhost and 8080 identify a running service. A ...

Replace only standalone TODO markers

Replacing every occurrence of TODO can alter text that merely contains those four letters. This example compiles r"\bTODO\b" , then replaces only matches bounded by the regular-expression engine’s word-boundary rule. It changes the leading TODO and the parenthesized TODO to DONE . It leaves preTODOpost unchanged because letters on both sides make the embedded text part of one word. It also leaves TODO_1 unchanged because underscore and digits are word characters. Pattern.subn() returns a pair containing the changed string and the number of replacements. The assertion checks both the complete output and the count of two before printing. That makes the sample’s intended matching behavior explicit, rather than deriving a count from displayed text. Python documents re.Pattern.subn() and \b word boundaries . These are established re APIs; the example requires Python 3 but no newer Python-version feature. A word boundary follows the regex engine’s definition of word char...

Report an invalid repeated separator with finditer

Delimited text can contain an accidental doubled separator even when the surrounding fields look plausible. This example treats adjacent identical colons or semicolons as invalid. The pattern captures one separator in the named separator group and then uses \1+ to require one or more immediate repetitions of that same character. Therefore :: and ;; are reported, while a single ; is not. Pattern.finditer yields match objects rather than only matched strings. Each object supplies the source text through group() and zero-based, end-exclusive offsets through start() and end() . The assertions establish the expected runs and offsets for this concrete input before the reporting loop formats each issue. End-exclusive offsets are convenient for slicing: text[start:end] retrieves the reported run exactly. This is a narrow validator, not a complete parser for a delimiter-based format. It deliberately does not reject mixed adjacent separators such as :; , separators inside quoted field...

Select XML children with a namespace map

XML namespace prefixes in a document are not the names that ElementTree stores internally. It expands namespaced tags to URI-qualified names. A namespace map lets a query use readable local prefixes while supplying the full namespace URI separately. This example parses a short catalog with one default namespace and one meta namespace, then calls findall() with paths that use the map. The map names are chosen by the program; they do not need to match the prefixes used in the XML source. root.findall('book:item', ns) selects the two default-namespace items. On each item, item.find('meta:code', ns) selects its namespaced child. The assertions confirm the two titles and codes, while the output pairs each title with its corresponding code in document order. This approach uses ElementTree’s supported XPath subset, not a complete XPath engine. A query must include the namespace map for each namespaced path, and an unqualified findall('item') would not sele...

Parse --verbose occurrences into a logging level

Repeated verbosity flags are a compact command-line convention, but counting them is separate from choosing logging behavior. argparse supplies action="count" to count occurrences of an option. This article maps that integer with a small, explicit policy: no flags means WARNING , one means INFO , and two or more mean DEBUG . The parser accepts both -v and --verbose . Its default=0 is important: without it, an omitted count option has the default value None . The example parses three synthetic argument lists: [] , [-v] , and [-vv] . argparse recognizes the grouped short form as two occurrences, then logging_level() converts the counts to standard logging constants. logging.getLevelName() makes the deterministic output readable: WARNING INFO DEBUG . The assertion verifies those three cases and the selected policy for them. This is not a universal verbosity scale. In particular, the function intentionally treats three or more occurrences the same as two; a program wanti...

Read a fallback value from DEFAULT in ConfigParser

ConfigParser has two similarly named mechanisms that behave differently. The special [DEFAULT] section supplies inherited option values to ordinary sections. The fallback= keyword of ConfigParser.get() is used only when the requested option is unavailable after normal lookup. A value in [DEFAULT] therefore takes precedence over a fallback= value. This example reads an INI string with color = teal in [DEFAULT] and a separate [ui] section. parser.get("ui", "color", fallback="gray") returns teal : the section does not define color , so lookup inherits it from DEFAULT . The missing font option is absent from both places, so parser.get("ui", "font", fallback="sans") returns sans . The tuple assertion checks both concrete results, and the two printed lines make that precedence visible. This inheritance can make configuration concise, but it also means a section may appear to contain an option that was not written under...

Stream XML records with iterparse

Stream XML records with iterparse ElementTree.iterparse() reports parsing events while it incrementally builds an XML tree. For record-oriented input, handle an "end" event for each completed record: at that point its children and text are available. This example uses an in-memory StringIO fixture containing three <record> elements, collects their identifiers and values, and calls clear() after each record has been read. The assertions demonstrate that each record produced the expected pair and that the numeric total is 23. element.clear() removes the processed element’s attributes, text, tail, and children, so it can reduce retained detail during this style of processing. It must occur only after extracting what the program needs; clearing earlier would make the child lookup fail. The printed output reports the three records and their aggregate. iterparse() performs blocking reads from its source. It is therefore not a non-blocking streaming interface, and it...

Reject a missing module spec before import

A module spec describes how the import system could load a module. importlib.util.find_spec() lets code ask for that description before deciding whether to import. Here, the concrete input is the intentionally nonexistent top-level name "article_fixture_missing_module_8f2c" . The expected result is None , so the program asserts that result, confirms the name is absent from sys.modules , and prints that importing was skipped. This is a useful branch for an optional, controlled dependency: call import_module() only when a spec was found. It is not a security boundary. Finders participate in discovery, and a spec's presence does not make a module trustworthy; importing a discovered module can still execute its top-level code. The test name is selected to avoid colliding with a normal standard-library module, but an environment with a custom importer could deliberately provide it. This check is also not atomic: the import environment can change between discovery and a late...

Use argparse BooleanOptionalAction for a local switch

A local on/off setting is often clearer when users can state either choice directly. argparse.BooleanOptionalAction creates the positive and negative long options from one declaration. Adding --cache with this action also accepts --no-cache ; the first stores True and the second stores False . The example sets default=True , which represents the application's chosen behavior when neither option appears. It then parses three fixed argument lists. An empty list keeps the default, --no-cache disables caching, and --cache explicitly enables it. The assertion checks that the resulting values are [True, False, True] , and print(*values) produces the exact output True False True . Passing lists directly to parse_args() keeps the sample independent of the program that launches it. This action is appropriate for a boolean setting, not a value-taking option. Avoid type=bool for text such as --cache False : Python considers non-empty strings truthy, so that spelling does not express...

Read HTML attributes without regex matching

HTML attributes have quoting, entity-reference, and case-normalization details that are easy to mishandle with a pattern aimed at raw source text. HTMLParser.handle_starttag() supplies a parsed list of (name, value) pairs, so this example reads an image element’s attributes directly instead of applying regular expressions. Converting those pairs to a dictionary makes named lookup clear for this controlled fixture. The input uses uppercase IMG and ALT , single quotes around src , and an entity in alt . The parser normalizes names to lowercase, removes quotation marks from values, and converts the &amp; reference. The assertions make each expected normalized result explicit, and the output is a stable joined summary: logo.svg | Blue & white . There are limits to the convenient dictionary step. HTML allows duplicate attribute names in source, but dict(attrs) keeps only the final value; use the original list if duplicate handling matters. An empty attribute is represented as...

Use re.escape for a literal search token

A search token is not automatically a literal regular expression. In this example, + and ? in a+b? have regex meanings, so compiling the token directly would describe a pattern instead of the four visible characters. re.escape(token) returns a pattern fragment in which characters with regex significance are escaped. The compiled expression then finds the two literal occurrences in the input and ignores the unrelated aab text. The assertions check both the matched strings and their zero-based spans. Spans are useful in diagnostics because they identify the precise part of the original string without modifying it. The printed count and first position make the result deterministic and easy to inspect. Use re.escape for data that must become a literal portion of a regex pattern, including a token inserted into a larger pattern. It does not validate the surrounding pattern or provide a replacement string for re.sub ; Python’s documentation specifically notes that replacement handlin...