python-hyper / uritemplate Goto Github PK
View Code? Open in Web Editor NEWURI template parsing per RFC6570
Home Page: https://uritemplate.readthedocs.io/en/latest/
License: Other
URI template parsing per RFC6570
Home Page: https://uritemplate.readthedocs.io/en/latest/
License: Other
As shown in an example here, it would be nice given uri template to match a fully expanded uri. For example given uri template
http://localhost/weather/{state}/{city}?forecast={day}
and
http://localhost/weather/Washington/Redmond?forecast=today
to get back a dictionary
{
'state': 'Washington',
'city': 'Redmond',
'day': 'today',
}
Hi!
Would you consider an API to validate whether a particular input was valid under RFC 6570 (or does such a thing exist already and I've missed it)?
E.g., http://example.com/dictionary/{term:1}/{term
is not a valid URI Template seemingly, but I cannot see an API that complains about that -- uritemplate.URITemplate
will happily truncate the end part there and consider that a template with just one field.
(Even having URITemplate do enough validation of its inputs would also work).
Full context: JSON Schema Draft 6 adds a uri-template
format. I'd love to use uritemplate
to implement it in jsonschema.
If your template variables contain /
, the expansion of the variable is an empty string.
from uritemplate import URITemplate
uri = URITemplate('http://example.com/{/student/id}')
uri.expand(var_dict={'/student/id': '0'})
print(uri) # Prints: http://example.com/
However, if you remove the first /
from the var_dict, you end up with a wrong value (/0
instead of 0
):
uri.expand(var_dict={'student/id': '0'})
print(uri) # Prints: http://example.com//0
Version: 4.1.1. Platform: Fedora 37 x86_64 (though this is not platform dependant).
tl;dr: With multiple query variables, URITemplate
appends a ?
instead of a &
. This is in violation of RFC6570 (IIUC).
Expected:
>>> URITemplate("http://example.org{?foo}{?bar}").expand(foo="this", bar="that")
'http://example.org?foo=this&bar=that'
(Second variable has a continuation.)
Actual:
>>> URITemplate("http://example.org{?foo}{?bar}").expand(foo="this", bar="that")
'http://example.org?foo=this?bar=that'
(Second variable has a question mark -- incorrect.)
See https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.8
For each defined variable in the variable-list:
o append "?" to the result string if this is the first defined value
or append "&" thereafter;
The form {&foo}
is only for those templates that have a literal ?
. See https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.9
Form-style query continuation, as indicated by the ampersand ("&")
operator in Level 3 and above templates, is useful for describing
optional &name=value pairs in a template that already contains a
literal query component with fixed parameters.
I can provide a patch later if required.
Let's look at a template like this:
http://example.com/dictionary/{term:1}
We have two different behaviours depending upon the value used to expand term
.
>>> import uritemplate
>>> u = uritemplate.URITemplate('http://example.com/dictionary/{term:1}')
>>> u.expand(term='foo')
'http://example.com/dictionary/f'
>>> u.expand(term=['foo', 'bar', 'bogus'])
'http://example.com/dictionary/foo,bar,bogus'
A simplified version of this is simply
{term:1}
In other words:
>>> import uritemplate
>>> u = uritemplate.URITemplate('{term:1}')
>>> u.expand(term='foo')
'f'
>>> u.expand(term=['foo', 'bar', 'bogus'])
'foo,bar,bogus'
All versions of uritemplate
(and uritemplate.py
) exhibit this behaviour and the RFC does not provide clear guidance.
I have not investigated how other implementations in other languages handle this, though. There do not appear to be any examples in the RFC that combine lists with length limitations.
Right now mypy flags this code is invalid:
from uritemplate import URITemplate
t = URITemplate('https://api.github.com{/end}')
t.expand({'end': None}) # error: Dict entry 0 has incompatible type "str": "None"; expected "str": "Union[Sequence[Union[int, float, complex, str]], Mapping[str, Union[int, float, complex, str]], complex]"
# Returns 'https://api.github.com'
I am not familiar with the API. Should this be allowed?
Since runtime works - I suspect that it might be worth considering.
It would be nice to have a release with #40 included so I'd stop getting the warnings ๐
See these test failures:
Hi guys, after the 4.0.0. or 4.0.1 update, a nested dependency (coreapi) using uritemplate without any version requirements causes the below exception.
webapp_1 | Traceback (most recent call last):
webapp_1 | File "/srv/django/manage.py", line 19, in <module>
webapp_1 | execute_from_command_line(sys.argv)
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 364, in execute_from_command_line
webapp_1 | utility.execute()
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 338, in execute
webapp_1 | django.setup()
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django/__init__.py", line 27, in setup
webapp_1 | apps.populate(settings.INSTALLED_APPS)
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django/apps/registry.py", line 85, in populate
webapp_1 | app_config = AppConfig.create(entry)
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django/apps/config.py", line 94, in create
webapp_1 | module = import_module(entry)
webapp_1 | File "/usr/local/lib/python2.7/importlib/__init__.py", line 37, in import_module
webapp_1 | __import__(name)
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django_filters/__init__.py", line 7, in <module>
webapp_1 | from .filterset import FilterSet
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django_filters/filterset.py", line 12, in <module>
webapp_1 | from .compat import remote_field, remote_queryset
webapp_1 | File "/usr/local/lib/python2.7/site-packages/django_filters/compat.py", line 28, in <module>
webapp_1 | import coreapi
webapp_1 | File "/usr/local/lib/python2.7/site-packages/coreapi/__init__.py", line 2, in <module>
webapp_1 | from coreapi import auth, codecs, exceptions, transports, utils
webapp_1 | File "/usr/local/lib/python2.7/site-packages/coreapi/transports/__init__.py", line 3, in <module>
webapp_1 | from coreapi.transports.http import HTTPTransport
webapp_1 | File "/usr/local/lib/python2.7/site-packages/coreapi/transports/http.py", line 13, in <module>
webapp_1 | import uritemplate
webapp_1 | File "/usr/local/lib/python2.7/site-packages/uritemplate/__init__.py", line 28, in <module>
webapp_1 | from uritemplate.api import (
webapp_1 | File "/usr/local/lib/python2.7/site-packages/uritemplate/api.py", line 19
webapp_1 | uri: str,
webapp_1 | ^
webapp_1 | SyntaxError: invalid syntax
I pinned the version to 3.0.1 to get around the issue.
uritemplate == 3.0.1
I realise this issue might not be in the right place, as this might be incorrect usage in coreapi (outside of not pinning the version to the right major version), but I guess other people might run into the same thing.
Best regards
i.e. given a URI and a template? does the URI match the template?
This would be a useful feature
In setup.cfg you are claiming that the newest version should work on Python 2 which is not true. Currently pip in Python 2 allows installing 4.1.0. Noticed this in our CI pipelines as we're installing django-rest-swagger==2.1.2
which has uritemplate (any version) as dependency.
>>> from uritemplate.api import (URITemplate, expand, partial, variables)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/julius/.virtualenvs/pcloud/lib/python2.7/site-packages/uritemplate/__init__.py", line 28, in <module>
from uritemplate.api import (
File "/Users/julius/.virtualenvs/pcloud/lib/python2.7/site-packages/uritemplate/api.py", line 19
uri: str,
^
SyntaxError: invalid syntax
A downstream package which uses jsonschema
gives me the following error when I try to run it:
Traceback (most recent call last):
File "/usr/lib/python-exec/python3.9/dandi", line 33, in <module>
sys.exit(load_entry_point('dandi==0+unknown', 'console_scripts', 'dandi')())
File "/usr/lib/python-exec/python3.9/dandi", line 25, in importlib_load_entry_point
return next(matches).load()
File "/usr/lib/python3.9/importlib/metadata.py", line 77, in load
module = import_module(match.group('module'))
File "/usr/lib/python3.9/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 850, in exec_module
File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
File "/usr/lib/python3.9/site-packages/dandi/cli/command.py", line 18, in <module>
from ..utils import get_module_version
File "/usr/lib/python3.9/site-packages/dandi/utils.py", line 25, in <module>
import requests
File "/usr/lib/python3.9/site-packages/requests/__init__.py", line 133, in <module>
from . import utils
File "/usr/lib/python3.9/site-packages/requests/utils.py", line 27, in <module>
from . import certs
File "/usr/lib/python3.9/site-packages/requests/certs.py", line 15, in <module>
from certifi import where
File "/usr/lib/python3.9/site-packages/certifi/__init__.py", line 1, in <module>
from .core import contents, where
File "/usr/lib/python3.9/site-packages/certifi/core.py", line 3, in <module>
from certifi._patch import _verify_dist_info
File "/usr/lib/python3.9/site-packages/certifi/_patch.py", line 4, in <module>
import pkg_resources
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3267, in <module>
def _initialize_master_working_set():
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3241, in _call_aside
f(*args, **kwargs)
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3279, in _initialize_master_working_set
working_set = WorkingSet._build_master()
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 573, in _build_master
ws.require(__requires__)
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 891, in require
needed = self.resolve(parse_requirements(requirements))
File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 777, in resolve
raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'uri-template; extra == "format"' distribution was not found and is required by jsonschema
Any idea what uritemplate install instructions are needed in order to fulfill this requirement?
I wonder if it makes any sense to add a way to validate templates.
https://uritemplate.readthedocs.io/en/latest/ still lists 3.0.0 as the latest version. https://readthedocs.org/projects/uritemplate/builds/ suggests there has not been a new build in 2 years, 5 months.
Valid UTF-8-percent-encoded code points must be preserved in reserved expansion. This implementation (Version: 4.1.1) fails the following test from https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json:
{
...
"Additional Examples 6: Reserved Expansion": {
"variables" : {
"id" : "admin%2F",
...
},
"testcases": [
["{+id}", "admin%2F"],
...
]
}
The correct/expected expansion is 'admin%2F'
, the actual expansion is 'admin%252F'
:
>>> from uritemplate import URITemplate, expand
>>> URITemplate('{+id}').expand(id='admin%2F')
'admin%252F'
>>> expand('{+id}', id='admin%2F')
'admin%252F'
Currently urlparse
can do this for semi-normal URIs that happen to be templates. Try for example parsing both of the following URIs
https://{server}/{path}
https://{server}{/path}
The first is handled fine, the second is parsed as: ('https', '{server}{', 'path}', '', '', '')
Properly parsing this could be useful and is born out of a desire to see how challenging it would be rather than a need for it.
I saw #17 that claimed that int/float vals should be supported and #20 that fixed it back in 2015. Imagine my surprise when I see that removed check in the traceback on passing int.
Looks like there's a similar place in code that hasn't been fixed for some reason and still causes trouble: https://github.com/python-hyper/uritemplate/blob/3.0.1/uritemplate/variable.py#L194
$ ipython
Python 3.7.1 (default, Jan 28 2019, 08:25:13)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.1.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import uritemplate
In [2]: uritemplate.URITemplate('/test{/num}').expand({'num': 3})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-5afe50171000> in <module>
----> 1 uritemplate.URITemplate('/test{/num}').expand({'num': 3})
~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/uritemplate/template.py in expand(self, var_dict, **kwargs)
130
131 """
--> 132 return self._expand(_merge(var_dict, kwargs), False)
133
134 def partial(self, var_dict=None, **kwargs):
~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/uritemplate/template.py in _expand(self, var_dict, replace)
95 expanded = {}
96 for v in self.variables:
---> 97 expanded.update(v.expand(expansion))
98
99 def replace_all(match):
~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/uritemplate/variable.py in expand(self, var_dict)
336 expansion = self._string_expansion
337
--> 338 expanded = expansion(name, value, opts['explode'], opts['prefix'])
339
340 if expanded is not None:
~/.pyenv/versions/3.7.1/lib/python3.7/site-packages/uritemplate/variable.py in _label_path_expansion(self, name, value, explode, prefix)
188 safe = self.safe
189
--> 190 if value is None or (len(value) == 0 and value != ''):
191 return None
192
TypeError: object of type 'int' has no len()
Not knowing this was inspired by an RFC, I was having a hard time finding some examples, eventually digging into the source to see how I could get foo/bar expanded into {/path}. A short list of examples wouldn't go amiss. Neither would a link to the RFC I guess ๐
Tag 0.2.0 is missing
There are two separate packages/projects with the name uritemplate, one is "uritemplate", the other is "uritemplate.py". It seems you are an index owner on both
On 8/20/16, a package from uritemplate.py was uploaded to the other project's index.
This project: https://pypi.python.org/pypi/uritemplate.py/2.0.0
Clobbered this project: https://pypi.python.org/pypi/uritemplate/0.6
With this pre-release version: https://pypi.python.org/pypi/uritemplate/2.0.0rc1
Can you remove the 2.0.0rc1 package from the other project please?
Thanks,
Hi,
For tests purpose, I created a "multi_expand" function, generating a bunch of URLs given a bunch of values: https://gist.github.com/JulienPalard/ee2f55716febad981d9740847b2f0d34
Tell me if you're interested by a pull request or prefer it packaged as a separated project.
Bests.
Looks like there have been a few changes since the last release (in 2013 ๐ณ) particularly from last year. Is it worthwhile to cut a new release to get those out? ๐
The RFC limits the characters allowed in variables names, but there is no validation enforcing this.
From ยง 2.3:
variable-list = varspec *( "," varspec )
varspec = varname [ modifier-level4 ]
varname = varchar *( ["."] varchar )
varchar = ALPHA / DIGIT / "_" / pct-encoded
(I'm not opposed to expanding the set of allowed characters.)
Seeing this deprecation warning when running a test suite that depends on uritemplate:
/home/myusername/venv/lib64/python3.7/site-packages/uritemplate/variable.py:363: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3,and in 3.9 it will stop working
return isinstance(value, (dict, collections.MutableMapping))
I get this error when I run the read me after pip installing uritemplate.py
I am running python 2.7.5
Traceback (most recent call last):
File "sandbox.py", line 5, in <module>
print(t.expand(gist_id=123456))
File "/Users/jpotts18/.virtualenvs/parakeet_env/lib/python2.7/site-packages/uritemplate/template.py", line 127, in expand
return self._expand(var_dict, False)
File "/Users/jpotts18/.virtualenvs/parakeet_env/lib/python2.7/site-packages/uritemplate/template.py", line 89, in _expand
expanded.update(v.expand(expansion))
File "/Users/jpotts18/.virtualenvs/parakeet_env/lib/python2.7/site-packages/uritemplate/variable.py", line 333, in expand
expanded = expansion(name, value, opts['explode'], opts['prefix'])
File "/Users/jpotts18/.virtualenvs/parakeet_env/lib/python2.7/site-packages/uritemplate/variable.py", line 185, in _label_path_expansion
if value is None or (len(value) == 0 and value != ''):
TypeError: object of type 'int' has no len()
I noticed the implementation allows specifying default values in the template, which is cool, but is not supported by the RFC.
Maybe this feature should be added to the RFC?
There is a package called uritemplate on the PyPI whose name conflicts with this package. Both packages require you to import uritemplate
.
I ran into this issue because our project requires both github3.py
(which depends on uritemplate.py
) and google-python-api-client
(which depends on uritemplate
), which led to the following error:
from uritemplate import URITemplate
ImportError: cannot import name URITemplate
I'm not sure what the best solution for this would be. Just thought I'd report it for record-keeping and to see if anyone had any workarounds.
@sigmavirus24, I like this project a lot. I noticed it's used by the Google API module for Python, so I took a look. I had just implemented a very limited version of URI templates for a requests-based project I've been working on. I didn't know anything about this project or the RFC upon which it's based. When my time allows, I'd like to contribute to this project, if you'd welcome my help.
I have a few questions, though (maybe some of which you'd like help resolving):
__init__.py
contains a vesion of "2.0.0".My apologies for dumping a bunch of things in this one message. I hope I don't seem to be complaining. I have a lot of respect for your contributions to flake8 and requests and this project seems to meet an important need, IMHO. I'd really like to contribute, so maybe these are things I could help with in addition to coding.
While integrating the tests from uri-templates/uritemplate-test it has "additional" tests which pass integers and floats.
This needs to be fixed to coerce the parameter to a string.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.