{"id":698,"library":"rfc3986","title":"rfc3986","description":"rfc3986 is a Python implementation of RFC 3986 including validation and authority parsing. This module also supports RFC 6874, which adds support for zone identifiers to IPv6 Addresses. It provides APIs for parsing, validating, and building URIs, with convenience methods for `urllib.parse` compatibility. The current version is 2.0.0, released in January 2022, and the project appears to be actively maintained.","status":"active","version":"2.0.0","language":"python","source_language":"en","source_url":"https://github.com/python-hyper/rfc3986","tags":["uri","url","rfc3986","validation","parsing","networking","hypermedia"],"install":[{"cmd":"pip install rfc3986","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Requires Python 3.7 or later.","package":"python","optional":false}],"imports":[{"note":"Use this for strict RFC 3986 compliant URI parsing and validation.","symbol":"uri_reference","correct":"from rfc3986 import uri_reference"},{"note":"Use this for API compatibility with Python's standard library `urllib.parse.urlparse`, but be aware of parsing differences for malformed URIs due to `rfc3986`'s strictness.","symbol":"urlparse","correct":"from rfc3986 import urlparse"},{"note":"Import the validators module to create custom URI validation rules.","symbol":"validators","correct":"from rfc3986 import validators"}],"quickstart":{"code":"from rfc3986 import uri_reference, validators\n\n# Parsing a URI Reference\nuri_str = 'https://user:pass@example.com:8080/path/to/resource?key=value#fragment'\nuri = uri_reference(uri_str)\n\nprint(f\"Scheme: {uri.scheme}\") # Output: https\nprint(f\"Host: {uri.host}\")     # Output: example.com\nprint(f\"Path: {uri.path}\")     # Output: /path/to/resource\nprint(f\"Query: {uri.query}\")   # Output: key=value\n\n# Validating a URI\nvalidator = validators.Validator().allow_schemes(['https']).allow_hosts(['example.com'])\n\nif validator.validate(uri):\n    print(\"URI is valid according to custom rules.\")\nelse:\n    print(\"URI is NOT valid according to custom rules.\")\n\n# Building a URI\nfrom rfc3986 import URIBuilder\n\nbuilder = (URIBuilder()\n           .add_scheme('mailto')\n           .add_path('user@domain.com'))\n\nmailto_uri = builder.finalize()\nprint(f\"Built URI: {mailto_uri.unsplit()}\") # Output: mailto:user@domain.com\n","lang":"python","description":"This quickstart demonstrates how to parse a URI string into a `URIReference` object, access its components, validate it using a `Validator` instance with custom rules, and construct a new URI using `URIBuilder`."},"warnings":[{"fix":"Always ensure URIs conform strictly to RFC 3986, especially by including `//` before the authority. For compatibility with `urllib.parse`'s looser parsing, use `rfc3986.urlparse()` but be aware of its limitations and the stricter RFC 3986 interpretation.","message":"rfc3986 strictly adheres to RFC 3986, which can result in different parsing behavior for malformed or non-standard URIs compared to `urllib.parse`. Specifically, an authority component must be preceded by `//`, otherwise, `rfc3986` may interpret it as part of the path.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If IRI support is required, consider using a different library (e.g., `rfc3987` or `uritools` with caution) or pre-process IRIs to ensure they are RFC 3986 compatible before passing them to `rfc3986`.","message":"The library does not support Internationalized Resource Identifiers (IRIs) as defined in RFC 3987. It focuses solely on RFC 3986.","severity":"gotcha","affected_versions":"All versions"},{"fix":"For critical security-sensitive URI validation, do not solely rely on `is_valid()`. Implement additional checks for hostname formats and port number ranges, or combine with a custom `Validator` instance that explicitly defines allowed components.","message":"The `uri_reference.is_valid()` method might, in some edge cases, accept invalid hostnames or out-of-range port numbers (according to open GitHub issues).","severity":"gotcha","affected_versions":"2.0.0"},{"fix":"If you need to extend path components or append to query parameters, retrieve the existing components, manipulate them as strings or lists, and then pass the complete new value to `copy_with`. Alternatively, use `URIBuilder` and its methods for more controlled incremental building.","message":"The `copy_with` method on parsed URI objects (from `uri_reference` or `urlparse`) replaces existing components rather than extending them. For example, adding a path segment replaces the entire path.","severity":"gotcha","affected_versions":"All versions"},{"fix":"To specify schemes for validation, pass each scheme as a separate argument (e.g., `validator.allow_schemes('https', 'http')`). If you have a list of schemes, unpack it using the `*` operator: `validator.allow_schemes(*['https', 'http'])`.","message":"The `allow_schemes()` method in `rfc3986.validators.Validator` expects individual scheme strings or an unpacked iterable as arguments. Passing a list object directly (e.g., `allow_schemes(['https'])`) results in an `AttributeError` when the library attempts to call `.lower()` on the list object instead of a string.","severity":"gotcha","affected_versions":"All versions"},{"fix":"When using `Validator.allow_schemes()`, provide each scheme as a separate string argument. If you have a list of schemes, unpack it using the `*` operator (e.g., `validator.allow_schemes(*my_schemes)` or `validator.allow_schemes('https')` for a single scheme).","message":"The `Validator.allow_schemes()` method expects scheme names as individual string arguments (e.g., `allow_schemes('https', 'http')`), not as a single list argument. Passing a list (e.g., `allow_schemes(['https'])`) will lead to an `AttributeError: 'list' object has no attribute 'lower'` when the method attempts to normalize the scheme.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T17:55:48.320Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"from rfc3986 import URIReference\ntry:\n    uri = URIReference.from_string('https://example.com/path') # Correct URI\nexcept URIReference.InvalidURIError as e:\n    print(f\"Error: {e}\")","cause":"The input string provided to `URIReference.from_string` does not conform to RFC 3986 standards for a valid URI, or a specific component is malformed.","error":"rfc3986.exceptions.InvalidURIError: Invalid scheme component: 'http://'"},{"fix":"from rfc3986 import URIReference\nuri = URIReference.from_string('https://example.com')","cause":"The function or class `parse_uri` does not exist at the top level of the `rfc3986` module; the primary method for parsing URIs is the `from_string` class method of `URIReference` (or `URI`).","error":"ImportError: cannot import name 'parse_uri' from 'rfc3986'"},{"fix":"from rfc3986 import URIReference\nuri = URIReference.from_string('/path/to/resource') # A relative URI without authority\nif uri.authority:\n    print(uri.authority.host)\nelse:\n    print(\"No authority component for this URI\")","cause":"This error occurs when attempting to access a sub-component (like `host`) on an optional URI part (like `authority`) that is `None` because it's not present in the parsed URI reference (e.g., for relative URIs).","error":"AttributeError: 'NoneType' object has no attribute 'host'"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":"2.0.0","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.12,"mem_mb":0.8,"disk_size":"18.0M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":0.8,"disk_size":"18.0M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.5,"import_time_s":0.07,"mem_mb":0.8,"disk_size":"19M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.09,"mem_mb":0.8,"disk_size":"19M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.31,"mem_mb":1,"disk_size":"19.9M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.38,"mem_mb":1,"disk_size":"19.9M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.6,"import_time_s":0.3,"mem_mb":1,"disk_size":"20M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.28,"mem_mb":1,"disk_size":"20M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.21,"mem_mb":0.8,"disk_size":"11.7M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.23,"mem_mb":0.8,"disk_size":"11.7M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.4,"import_time_s":0.24,"mem_mb":0.8,"disk_size":"12M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.26,"mem_mb":0.8,"disk_size":"12M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.19,"mem_mb":1,"disk_size":"11.5M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.29,"mem_mb":1,"disk_size":"11.4M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.6,"import_time_s":0.21,"mem_mb":0.8,"disk_size":"12M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.23,"mem_mb":0.8,"disk_size":"12M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":0.8,"disk_size":"17.5M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":0.8,"disk_size":"17.5M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.7,"import_time_s":0.09,"mem_mb":0.8,"disk_size":"18M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":0.8,"disk_size":"18M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}