# furl keeps URL parts as mutable objects, so path segments stay decoded and fragments get their own path and query

> gruns/furl is a small Python library for reading and editing URLs. Its model splits a URL into scheme, host, port, a Path object, a Query object and a Fragment that has a path and query of its own.

**gruns/furl** — 🌐 The easiest way to parse and modify URLs in Python.

- Repository: https://github.com/gruns/furl
- Stars: 2,812 · Forks: 174
- Language: Python
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/gruns-furl

## A furl object edits URL parts in place instead of rebuilding a string

Python's urllib and urlparse modules do offer URL functions, and the project's own pitch is that using them for common operations proves tedious. furl answers with one object whose attributes you assign to. This is the first usage example:

```python
>>> from furl import furl
>>> f = furl('http://www.google.com/?one=1&two=2')
>>> f /= 'path'
>>> del f.args['one']
>>> f.args['three'] = '3'
>>> f.url
'http://www.google.com/path?two=2&three=3'
```

The /= operator appends a path segment, del removes a query argument, and assignment into f.args adds one. The result is read back from f.url. Nothing is reparsed between steps because the parts live on the object.

The README describes furl as small, well tested, and supporting Python 3 and PyPy3.

## Installation is a single pip command

The package is on PyPI under the name furl:

```
$ pip install furl
```

There is no extra dependency list in the README to follow. The repository also contains setup.py, tox.ini, a tests directory and changelog.txt, so the usual route for contributors is tox, which the setup.py docstring says tests all supported Python versions in parallel with tox -p.

The README is explicit that the supported interpreters are Python 3 and PyPy3. A project pinned to Python 2 is outside what it claims.

## None, an empty string and a missing port each mean something different

The basics section lays out the URL as scheme://username:password@host:port/path?query#fragment and defines each part with care. A scheme of None means there is no scheme. A scheme that is an empty string means a protocol relative URL such as //www.google.com. The port is an integer or None, and None means the default port for the scheme should be inferred if possible.

The inference is concrete. An https URL reports port 443. An unknown scheme such as unknown:// reports None, because furl has no default to infer. The README example prints exactly that.

Domain names come back in lowercase, and so does the scheme. If you compare hosts you got from furl against a string with capitals, the case has already been changed.

## netloc and origin leave out a port that is None or the scheme default

Two derived strings are provided. netloc combines username, password, host and port. origin combines scheme, host and port. Both omit the port when it is None or equal to the default for the scheme.

So furl('http://www.google.com/').netloc returns 'www.google.com', while furl('http://www.google.com:99/').netloc returns 'www.google.com:99', and the version with credentials returns 'user:pass@www.google.com:99'. The origin of the plain http URL is 'http://www.google.com'.

The practical use is comparison. Two URLs that differ only by an explicit :80 on http produce the same origin, which is the cheaper way to ask whether they point at the same site than comparing raw strings.

## Path segments are stored decoded, and URL paths with a netloc must be absolute

A Path object has segments, a list that can be edited directly. The segments are percent-decoded, and all interaction with them should be with decoded strings. For a URL ending in /a/large%20ish/path, f.path.segments is ['a', 'large ish', 'path'], and str(f.path) gives back '/a/large%20ish/path'.

Setting segments to ['segments', 'are', 'maintained', 'decoded', '^`<>[]"#/?'] produces a string path with each of those characters percent-encoded. You never encode by hand.

There is one restriction that bites. A Path has an isabsolute attribute, and a URL path must be absolute whenever a netloc is present, because the path has to start with / to separate itself from the netloc. In that case isabsolute becomes True and read-only, and assigning False raises an AttributeError. Fragment paths have no such limit.

## Fragments carry a path and a query of their own

Most URL libraries treat the fragment as a single string. In furl the fragment object holds a Path and a Query separated by an optional ? separator. The README example sets f.fragment.path.segments to ['two', 'directories'] and f.fragment.args to {'one': 'argument'}, and the url becomes 'http://www.google.com/#two/directories?one=argument'.

That shape suits single-page apps that put a route and parameters after the hash. If your fragment is an opaque token, you are still working through path and args objects to get at it.

Encoding applies across all of these parts. Spaces in a path become %20, spaces in query keys become +, and Unicode hosts are converted to the punycode form, as in xn--eckwd4c7c.xn--zckzah in the README example.

## Maintenance and license: last push 2026-10-19, last release v2.1.4

The repository is not archived. Its last push was on 2026-10-19. The latest tagged release is v2.1.4 from 2025-03-09, and before that v2.1.2 on 2021-04-12 and v2.1.1 on 2021-04-09, so tags are infrequent.

The README says furl is maintained by Aleksandre Cochran, with support from Confidential AI. The default branch is master.

The license needs a manual check. The README links the Unlicense and says furl is Unlicensed in the public domain, and the setup.py header says License: Build Amazing Things (Unlicense). GitHub's license field shows NOASSERTION for the repository. If your company scans dependencies by detected license, the scanner may flag it and you will need to point at the README and setup.py.

## Conclusion

Use furl when you build or rewrite URLs by hand and the arithmetic with urllib.parse has become tedious: appending path segments, swapping query arguments, or keeping encoding correct. Skip it if you only need to split a URL once and read one field. Before depending on it, check two things: the license line, since the README calls it Unlicensed and public domain while GitHub reports no recognized license, and the release date, since the latest tagged release is v2.1.4 from 2025-03-09.

## FAQ

### What is furl in Python?

furl is a small Python library for parsing and modifying URLs. It exposes scheme, host, port, path, query and fragment as objects you can edit.

### How do I install furl?

Run pip install furl. The README lists support for Python 3 and PyPy3.

### What license does furl use?

The README says furl is Unlicensed and in the public domain, and setup.py names the Unlicense. GitHub reports the license as NOASSERTION.

## Sources

- [gruns/furl on GitHub](https://github.com/gruns/furl)
- [Issues](https://github.com/gruns/furl/issues)
- [README](https://github.com/gruns/furl/blob/master/README.md)
- [Releases](https://github.com/gruns/furl/releases)

---

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