{"id":13502653,"url":"https://github.com/codeinthehole/purl","last_synced_at":"2025-10-20T16:21:12.294Z","repository":{"id":648332,"uuid":"3848656","full_name":"codeinthehole/purl","owner":"codeinthehole","description":"A simple, immutable URL class with a clean API for interrogation and manipulation.","archived":false,"fork":false,"pushed_at":"2023-08-04T07:41:28.000Z","size":168,"stargazers_count":294,"open_issues_count":14,"forks_count":39,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-09-23T16:08:14.579Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://codeinthehole.com/writing/purl-immutable-url-objects-for-python/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codeinthehole.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2012-03-27T21:18:43.000Z","updated_at":"2024-09-07T17:13:58.000Z","dependencies_parsed_at":"2023-01-13T10:33:31.350Z","dependency_job_id":"13c9cdcc-7a33-419a-b075-9df2f7280820","html_url":"https://github.com/codeinthehole/purl","commit_stats":{"total_commits":167,"total_committers":18,"mean_commits":9.277777777777779,"dds":"0.14970059880239517","last_synced_commit":"2bd51cabecfd4dcd20544fba7092cfd98dc7dac0"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeinthehole%2Fpurl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeinthehole%2Fpurl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeinthehole%2Fpurl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codeinthehole%2Fpurl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codeinthehole","download_url":"https://codeload.github.com/codeinthehole/purl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222495114,"owners_count":16993285,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T22:02:21.237Z","updated_at":"2025-10-20T16:21:07.240Z","avatar_url":"https://github.com/codeinthehole.png","language":"Python","readme":"================================\npurl - A simple Python URL class\n================================\n\nA simple, immutable URL class with a clean API for interrogation and\nmanipulation.  Supports Pythons 2.7, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8 and pypy.\n\nAlso supports template URLs as per `RFC 6570`_\n\nContents:\n\n.. contents:: :local:\n    :depth: 1\n\n.. image:: https://secure.travis-ci.org/codeinthehole/purl.png\n    :target: https://travis-ci.org/codeinthehole/purl\n\n.. image:: https://img.shields.io/pypi/v/purl.svg\n    :target: https://crate.io/packages/purl/\n\n.. _`RFC 6570`: http://tools.ietf.org/html/rfc6570\n\nDocs\n----\n\nhttp://purl.readthedocs.org/en/latest/\n\nInstall\n-------\n\nFrom PyPI (stable)::\n\n    $ pip install purl\n\nFrom Github (unstable)::\n\n    $ pip install git+git://github.com/codeinthehole/purl.git#egg=purl\n\nUse\n---\n\nConstruct:\n\n.. code:: python\n\n    \u003e\u003e\u003e from purl import URL\n\n    # String constructor\n    \u003e\u003e\u003e from_str = URL('https://www.google.com/search?q=testing')\n\n    # Keyword constructor\n    \u003e\u003e\u003e from_kwargs = URL(scheme='https', host='www.google.com', path='/search', query='q=testing')\n\n    # Combine\n    \u003e\u003e\u003e from_combo = URL('https://www.google.com').path('search').query_param('q', 'testing')\n\nURL objects are immutable - all mutator methods return a new instance.\n\nInterrogate:\n\n.. code:: python\n\n    \u003e\u003e\u003e u = URL('https://www.google.com/search?q=testing')\n    \u003e\u003e\u003e u.scheme()\n    'https'\n    \u003e\u003e\u003e u.host()\n    'www.google.com'\n    \u003e\u003e\u003e u.domain()\n    'www.google.com'\n    \u003e\u003e\u003e u.username()\n    \u003e\u003e\u003e u.password()\n    \u003e\u003e\u003e u.netloc()\n    'www.google.com'\n    \u003e\u003e\u003e u.port()\n    \u003e\u003e\u003e u.path()\n    '/search'\n    \u003e\u003e\u003e u.query()\n    'q=testing'\n    \u003e\u003e\u003e u.fragment()\n    ''\n    \u003e\u003e\u003e u.path_segment(0)\n    'search'\n    \u003e\u003e\u003e u.path_segments()\n    ('search',)\n    \u003e\u003e\u003e u.query_param('q')\n    'testing'\n    \u003e\u003e\u003e u.query_param('q', as_list=True)\n    ['testing']\n    \u003e\u003e\u003e u.query_param('lang', default='GB')\n    'GB'\n    \u003e\u003e\u003e u.query_params()\n    {'q': ['testing']}\n    \u003e\u003e\u003e u.has_query_param('q')\n    True\n    \u003e\u003e\u003e u.has_query_params(('q', 'r'))\n    False\n    \u003e\u003e\u003e u.subdomains()\n    ['www', 'google', 'com']\n    \u003e\u003e\u003e u.subdomain(0)\n    'www'\n\nNote that each accessor method is overloaded to be a mutator method too, similar\nto the jQuery API.  Eg:\n\n.. code:: python\n\n    \u003e\u003e\u003e u = URL.from_string('https://github.com/codeinthehole')\n\n    # Access\n    \u003e\u003e\u003e u.path_segment(0)\n    'codeinthehole'\n\n    # Mutate (creates a new instance)\n    \u003e\u003e\u003e new_url = u.path_segment(0, 'tangentlabs')\n    \u003e\u003e\u003e new_url is u\n    False\n    \u003e\u003e\u003e new_url.path_segment(0)\n    'tangentlabs'\n\nHence, you can build a URL up in steps:\n\n.. code:: python\n\n    \u003e\u003e\u003e u = URL().scheme('http').domain('www.example.com').path('/some/path').query_param('q', 'search term')\n    \u003e\u003e\u003e u.as_string()\n    'http://www.example.com/some/path?q=search+term'\n\nAlong with the above overloaded methods, there is also a ``add_path_segment``\nmethod for adding a segment at the end of the current path:\n\n.. code:: python\n\n    \u003e\u003e\u003e new_url = u.add_path_segment('here')\n    \u003e\u003e\u003e new_url.as_string()\n    'http://www.example.com/some/path/here?q=search+term'\n\nCouple of other things:\n\n* Since the URL class is immutable it can be used as a key in a dictionary\n* It can be pickled and restored\n* It supports equality operations\n* It supports equality operations\n\nURL templates can be used either via a ``Template`` class:\n\n.. code:: python\n\n    \u003e\u003e\u003e from purl import Template\n    \u003e\u003e\u003e tpl = Template(\"http://example.com{/list*}\")\n    \u003e\u003e\u003e url = tpl.expand({'list': ['red', 'green', 'blue']})\n    \u003e\u003e\u003e url.as_string()\n    'http://example.com/red/green/blue'\n\nor the ``expand`` function:\n\n.. code:: python\n\n    \u003e\u003e\u003e from purl import expand\n    \u003e\u003e\u003e expand(u\"{/list*}\", {'list': ['red', 'green', 'blue']})\n    '/red/green/blue'\n\nA wide variety of expansions are possible - refer to the RFC_ for more details.\n\n.. _RFC: http://tools.ietf.org/html/rfc6570\n\nChangelog\n---------\n\nv1.6 - 2021-05-15\n~~~~~~~~~~~~~~~~~\n\n* Use `pytest` insteed of `nose`.\n* Fix warning around regex string.\n\nv1.5 - 2019-03-10\n~~~~~~~~~~~~~~~~~\n\n* Allow `@` in passwords.\n\nv1.4 - 2018-03-11\n~~~~~~~~~~~~~~~~~\n\n* Allow usernames and passwords to be removed from URLs.\n\nv1.3.1\n~~~~~~\n\n* Ensure paths always have a leading slash.\n\nv1.3\n~~~~\n\n* Allow absolute URLs to be converted into relative.\n\nv1.2\n~~~~\n\n* Support password-less URLs.\n* Allow slashes to be passed as path segments.\n\nv1.1\n~~~~\n\n* Support setting username and password via mutator methods\n\nv1.0.3\n~~~~~~\n\n* Handle some unicode compatibility edge-cases\n\nv1.0.2\n~~~~~~\n\n* Fix template expansion bug with no matching variables being passed in. This\n  ensures ``purl.Template`` works correctly with the URLs returned from the\n  Github API.\n\nv1.0.1\n~~~~~~\n\n* Fix bug with special characters in paths not being escaped.\n\nv1.0\n~~~~\n\n* Slight tidy up. Document support for PyPy and Python 3.4.\n\nv0.8\n~~~~\n\n* Support for RFC 6570 URI templates\n\nv0.7\n~~~~\n\n* All internal strings are unicode.\n* Support for unicode chars in path, fragment, query, auth added.\n\nv0.6\n~~~~\n\n* Added ``append_query_param`` method\n* Added ``remove_query_param`` method\n\nv0.5\n~~~~\n\n* Added support for Python 3.2/3.3 (thanks @pmcnr and @mitchellrj)\n\nv0.4.1\n~~~~~~\n\n* Added API docs\n* Added to readthedocs.org\n\nv0.4\n~~~~\n\n* Modified constructor to accept full URL string as first arg\n* Added ``add_path_segment`` method\n\nv0.3.2\n~~~~~~\n\n* Fixed bug port number in string when using from_string constructor\n\nv0.3.1\n~~~~~~\n\n* Fixed bug with passing lists to query param setter methods\n\nv0.3\n~~~~\n\n* Added support for comparison and equality\n* Added support for pickling\n* Added ``__slots__`` so instances can be used as keys within dictionaries\n\nContribute\n----------\n\nClone, create a virtualenv then install purl and the packages required for\ntesting::\n\n    $ git clone git@github.com:codeinthehole/purl.git\n    $ cd purl\n    $ mkvirtualenv purl  # requires virtualenvwrapper\n    (purl) $ make\n\nEnsure tests pass using::\n\n    (purl) $ pytest\n\nor::\n\n    $ tox\n","funding_links":[],"categories":["URL Manipulation","资源列表","Python","URL操作","URL 处理","URL Manipulation [🔝](#readme)","Awesome Python"],"sub_categories":["URL 处理","URL Manipulation"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeinthehole%2Fpurl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodeinthehole%2Fpurl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodeinthehole%2Fpurl/lists"}