# parse: the opposite of format, for pulling values back out of a string

> parse is a Python library that parses strings using a specification based on the format() syntax, so the same curly brace template you would use to build a string can be used to take one apart. The module exports only parse, search, findall and with_pattern on a star import, and it deliberately does not export compile, which would shadow the builtin.

**r1chardj0n3s/parse** — Parse strings using a specification based on the Python format() syntax.

- Repository: https://github.com/r1chardj0n3s/parse
- Website: http://pypi.python.org/pypi/parse
- Stars: 1,798 · Forks: 127
- Language: Python
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/r1chardj0n3s-parse

## What it does: one template in both directions

parse is described as a library that parses strings using a specification based on the Python format() syntax, and the summary line in the package metadata is shorter still, parse() is the opposite of format(). That framing is the whole idea. The template you would use to build a string, with curly braces standing in for values, is the template you use to take the string apart. So parse("It's {}, I love it!", "It's spam, I love it!") returns a Result holding the tuple of matched values and the dictionary of named values, and the positional values are also available by index. The module is set up to only export parse(), search(), findall() and with_pattern() when import * is used, which is a deliberate narrowing of the public surface. The project is MIT licensed, the primary language is Python, and the last push was on 2026-10-10 with the release 1.22.3 on the same day.

```python
>>> from parse import *

>>> parse("It's {}, I love it!", "It's spam, I love it!")
<Result ('spam',) {}>
>>> _[0]
'spam'
```

## Three ways to match: parse, search and findall

The module covers three distinct matching needs, and the difference is the usual one between a full match, a search and a scan. parse matches the whole string against the template, and if it does not fit you get None. search looks for the pattern within a longer string, so search('Age: {:d}\n', 'Name: Rufus\nAge: 42\nColor: red\n') returns a Result with the value 42 and nothing else, which is the shape you want for pulling one field out of a log line. findall returns every occurrence, and the documented example joins the results to reassemble the text between angle brackets, ''.join(r[0] for r in findall(">{}<", "<p>the <b>bold</b> text</p>")) producing the bold text. with_pattern is the fourth export, and it lets you attach a compiled pattern to a string so the same template can be reused, which is the mechanism behind the compile function.

```python
>>> search('Age: {:d}\n', 'Name: Rufus\nAge: 42\nColor: red\n')
<Result (42,) {}>

>>> ''.join(r[0] for r in findall(">{}<", "<p>the <b>bold</b> text</p>"))
'the bold text'
```

## Compiling a pattern once, and why compile is not exported

If you are going to use the same pattern to match lots of strings you can compile it once, and the example shows the compile function from the parse module, taking the template and returning a Parser object whose repr is the template itself, so you can print it and see what you built. The compiled parser has the same parse method, and the same result type. The one detail that trips people up is explained in a parenthetical in the README: compile is not exported for import * usage as it would override the built-in compile() function. That is a small and correct decision, since a star import shadowing a builtin is a genuine hazard, and the cost is that you have to import it by name. The result type is also richer than a tuple, since it supports both indexing and named access.

```python
>>> from parse import compile
>>> p = compile("It's {}, I love it!")
>>> print(p)
<Parser "It's {}, I love it!">
>>> p.parse("It's spam, I love it!")
<Result ('spam',) {}>
```

## The result object, and the in operator that only works for named fields

A Result holds two things, the fixed positional values and the named values, and both are reachable in more than one way. The fixed attribute gives the tuple of positional values, indexing with r[0] gives the first and slicing with r[1:] gives the rest, and the named attribute gives a dict which is also reachable by subscript. There is a subtlety the README flags: the in operator only works if you have named fields, so 'item' in r is True for a named field and raises for a purely positional one. The result of parsing a template with a named field but no positional one is a Result whose fixed tuple is empty and whose named dict holds the value. The other thing to know is that numbered fields are not supported, unlike format(), and the result includes the parsed fields in the order they were parsed.

```python
>>> r = parse("Bring out the holy {item}", "Bring out the holy hand grenade")
>>> print(r)
<Result () {'item': 'hand grenade'}>
>>> print(r.named)
{'item': 'hand grenade'}
>>> 'item' in r
True
```

## Dotted names and index subscripts, with the limits stated

Dotted names and indexes are possible with some limits, and the limits are explicit: only word identifiers are supported, so no numeric indexes, and the application must make additional sense of the result. A dotted name is kept as a single key, so parsing "Mmm, {food.type}, I love it!" against "Mmm, spam, I love it!" gives a named dict of {'food.type': 'spam'}, which you access with r['food.type'] and which is not split into a nested structure. The bracket subscript is the way to get nesting, so parsing "My quest is {quest[name]}" against "My quest is to seek the holy grail!" produces a nested dict under the key 'quest', with the 'name' key inside it, reachable as r['quest']['name']. If the text you are matching has braces in it, you match those by including a double-brace {{ or }} in the format string, the same escaping method used in the format() syntax.

```python
>>> r = parse("My quest is {quest[name]}", "My quest is to seek the holy grail!")
>>> print(r['quest'])
{'name': 'to seek the holy grail!'}
>>> print(r['quest']['name'])
to seek the holy grail!
```

## The type spec, and where it differs from format()

Most of format()'s format specification mini-language is supported, in the form [[fill]align][sign][0][width][grouping][.precision][type], but the differences are where the interesting behaviour lives. The align operators cause spaces or the specified fill character to be stripped from the parsed value, and the width is not enforced, it just indicates there may be whitespace or zeros to strip. Numeric parsing automatically handles a 0b, 0o or 0x prefix, so the # format character is handled automatically by the d, b, o and x formats, with the correct prefix required for the others if present at all. Numeric sign is handled automatically and a sign specifier has no effect. The thousands separator is handled automatically for the n type. The type list is a slightly different mix, with d, n, %, f, e, b, o and x coming directly from format(), plus the regular expression character group types D, w, W, s and S.

## The type table, from letters to decimal and dates

The type table is the reference to keep open, because the mapping from type to output type is not always what you would guess. The l type matches letters in ASCII and gives a str, w matches letters, numbers and underscore, W matches the inverse, s matches whitespace and S matches non-whitespace. The d type matches integer numbers with an optional sign and gives an int, D matches non-digits as a str, and n matches numbers with thousands separators, either comma or dot, giving an int. The percent type converts to a value divided by 100.0 as a float, f matches fixed-point numbers as a float, and F matches decimal numbers giving a Decimal, which is the one to reach for when the value is money. The e type handles floating-point numbers with an exponent, giving a float, and it is case-insensitive so there is no need for an E type, and it handles Fortran formatted numbers with no leading zero before the decimal point. Then there is g for a general number format, b for binary, o for octal and x for hexadecimal, all as ints, and four date and time types, ti for ISO 8601, te for RFC2822 email format, tg for the global day/month format and ta for the US month/day format, all giving datetime objects.

## Case sensitivity by default, and the packaging and test setup

One behaviour catches people out because it is the opposite of what regex users expect. The default behaviour is to match strings case insensitively, and you match with case by specifying case_sensitive=True, so parse('SPAM', 'spam', case_sensitive=True) is None. The project metadata shows a modern build, setuptools 61.2 or newer as the build requirement, the version read dynamically from the parse.__version__ attribute, a universal wheel flag, and the package described as parse() is the opposite of format(). The pytest configuration is the part worth noting, since addopts includes --cov=parse with term-missing and branch coverage, plus --doctest-modules and --doctest-glob=README.rst, which means the README examples are executed as tests and the package modules are doctested too. So the documentation cannot drift from the behaviour without failing the suite, and the maintainers list is split between the author, Richard Jones, and a maintainer, Wim Jeantine-Glenn.

## Conclusion

parse suits a codebase that pulls values out of log lines, identifiers, headers or semi-structured text and would rather write one template than a regular expression, and that wants the type conversion implied by the template rather than doing it afterwards. It is the wrong choice when the text has no stable shape, since a template requires you to know the format in advance, and a poor fit for a project that would rather use the standard library's own parsing facilities. Before adopting it, note that the default behaviour is to match strings case insensitively and that the type conversion happens from the format spec, and that the pytest configuration treats the README as a doctest, so the examples in the documentation are executable tests.

## FAQ

### What does the parse library do?

It parses strings using a specification based on the Python format() syntax, so a template written in curly braces can be used to extract values from a string. The module exports only parse, search, findall and with_pattern on a star import, and compile is importable by name but not exported, because it would shadow the builtin.

### What is the difference between parse, search and findall?

parse matches the whole string against the template and returns None if it does not fit, search looks for the pattern within a longer string such as a log line, and findall returns every occurrence of the pattern in the string.

### Does parse match case sensitively?

No, the default behaviour is to match strings case insensitively. You match with case by specifying case_sensitive=True, so parse('SPAM', 'spam', case_sensitive=True) returns None.

### How do I get nested values out of a parse result?

Use a bracket subscript in the template, so {quest[name]} produces a nested dict under the 'quest' key that you reach with r['quest']['name']. A dotted name such as {food.type} is kept as a single key, and only word identifiers are supported, with no numeric indexes.

### Which types does parse support?

Most of format()'s mini-language, with d, n, %, f, e, b, o and x carried over, plus the regular expression group types D, w, W, s and S, a Decimal-producing F type, and four date and time types, ti for ISO 8601, te for RFC2822 email format, tg for global day/month and ta for US month/day.

## Sources

- [License: MIT](https://github.com/r1chardj0n3s/parse/blob/master/LICENSE)
- [Project website](http://pypi.python.org/pypi/parse)
- [r1chardj0n3s/parse on GitHub](https://github.com/r1chardj0n3s/parse)
- [README](https://github.com/r1chardj0n3s/parse/blob/master/README.md)
- [Releases](https://github.com/r1chardj0n3s/parse/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/r1chardj0n3s-parse
