def test_matcher_functional_newstyle(self): self.matches('/{x}', '', None) self.matches('/{x}', '/', None) self.matches('/abc/{def}', '/abc/', None) self.matches('/{x}', '/a', {'x':'a'}) self.matches('zzz/{x}', '/zzz/abc', {'x':'abc'}) self.matches('zzz/{x}*traverse', '/zzz/abc', {'x':'abc', 'traverse':()}) self.matches('zzz/{x}*traverse', '/zzz/abc/def/g', {'x':'abc', 'traverse':('def', 'g')}) self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')}) self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')}) #'/La%20Pe%C3%B1a' self.matches('{x}', native_(b'/La Pe\xc3\xb1a'), {'x':text_(b'La Pe\xf1a')}) # '/La%20Pe%C3%B1a/x' self.matches('*traverse', native_(b'/La Pe\xc3\xb1a/x'), {'traverse':(text_(b'La Pe\xf1a'), 'x')}) self.matches('/foo/{id}.html', '/foo/bar.html', {'id':'bar'}) self.matches('/{num:[0-9]+}/*traverse', '/555/abc/def', {'num':'555', 'traverse':('abc', 'def')}) self.matches('/{num:[0-9]*}/*traverse', '/555/abc/def', {'num':'555', 'traverse':('abc', 'def')}) self.matches('zzz/{_}', '/zzz/abc', {'_':'abc'}) self.matches('zzz/{_abc}', '/zzz/abc', {'_abc':'abc'}) self.matches('zzz/{abc_def}', '/zzz/abc', {'abc_def':'abc'})
def oauth_response(result): headers, body, status = result return Response( body=body, status=status, headers={ native_(name, encoding="latin-1"): native_(value, encoding="latin-1") for name, value in headers.items() }, )
def oauth_response(result): headers, body, status = result headerlist = headers.items() if hasattr(headers, 'items') else headers return Response(body=body, status=status, charset='utf-8', headerlist=[ (native_(name, encoding='latin-1'), native_(value, encoding='latin-1')) for name, value in headerlist ])
def oauth_response(result): headers, body, status = result if isinstance(headers, dict): header_iter = headers.items() else: header_iter = headers return Response(body=body, status=status, headerlist=[ (native_(name, encoding='latin-1'), native_(value, encoding='latin-1')) for name, value in header_iter ])
def test_post_collection_warning_exception(self): # First POST - get back a 307. res1 = self.app.post(self.path, params='foo name', status=307) body_text = native_(res1.body.rstrip(), encoding='utf-8') self.assert_true(body_text.endswith( UserMessagePostCollectionView.message)) self.assert_true(res1.body.startswith(b'307 Temporary Redirect')) # Second POST to redirection location - get back a 201. resubmit_location1 = res1.headers['Location'] res2 = self.app.post(resubmit_location1, params='foo name', status=201) self.assert_true(not res2 is None) # Third POST to same redirection location with different warning # message triggers a 307 again. old_msg = UserMessagePostCollectionView.message UserMessagePostCollectionView.message = old_msg[::-1] try: res3 = self.app.post(resubmit_location1, params='foo name', status=307) self.assert_true(res3.body.startswith(b'307 Temporary Redirect')) # Fourth POST to new redirection location - get back a 409 (since # the second POST from above went through). resubmit_location2 = res3.headers['Location'] res4 = self.app.post(resubmit_location2, params='foo name', status=409) self.assert_true(not res4 is None) finally: UserMessagePostCollectionView.message = old_msg
def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() uc = text_(b"La Pe\xc3\xb1a", "utf-8") result = request.resource_url(context, anchor=uc) self.assertEqual(result, native_(text_(b"http://example.com:5432/context/#La Pe\xc3\xb1a", "utf-8"), "utf-8"))
def template_renderer(self, content, vars, filename=None): content = native_(content, fsenc) try: return bytes_( substitute_double_braces(content, TypeMapper(vars)), fsenc) except Exception as e: _add_except(e, ' in file %s' % filename) raise
def generate_secret(length=40): """ Returns a random ascii hash of the specified length (default is 40). .. note:: Due to the way the secret is generated, ``length`` should always be an even number. """ return native_(binascii.hexlify(os.urandom(int(length / 2))))
def pre(self, command, output_dir, vars): vars["random_string"] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars["package"] if package_logger == "root": # Rename the app logger in the rare case a project is named 'root' package_logger = "app" vars["package_logger"] = package_logger return Template.pre(self, command, output_dir, vars)
def test_get_collection_with_refs_options(self): # The links options are not processed by the renderers, so we need # a native everest view with a defined response MIME type. self.config.add_resource_view(IMyEntity, default_response_content_type=CsvMime, request_method=RequestMethods.GET) create_collection() res1 = self.app.get(self.path, params=dict(refs='parent:OFF'), status=200) self.assert_is_not_none(res1) self.assert_equal(native_(res1.body).find(',"parent",'), -1) self.assert_equal(native_(res1.body).find(',"parent.id",'), -1) res2 = self.app.get(self.path, params=dict(refs='parent:INLINE'), status=200) self.assert_is_not_none(res2) self.assert_equal(native_(res2.body).find(',"parent",'), -1) self.assert_not_equal(native_(res2.body).find(',"parent.id",'), -1)
def pre(self, command, output_dir, vars): vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars)
def test_route_url_with_anchor_binary(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute("/1/2/3")) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url("flub", _anchor=b"La Pe\xc3\xb1a") self.assertEqual(result, native_(text_(b"http://example.com:5432/1/2/3#La Pe\xc3\xb1a", "utf-8"), "utf-8"))
def test_non_utf8_path_segment_settings_unicode_path_segments_fails(self): from pyramid.exceptions import URLDecodeError foo = DummyContext() root = DummyContext(foo) policy = self._makeOne(root) segment = native_(text_(b'LaPe\xc3\xb1a', 'utf-8'), 'utf-16') environ = self._getEnviron(PATH_INFO='/%s' % segment) request = DummyRequest(environ) self.assertRaises(URLDecodeError, policy, request)
def normalize(title): """ make an URL resource name ready for use in a URL. Essentially it takes a string representing an id or title and makes it character safe for use in a URL. In ``lumin`` this is likely to be the :term:`_id` or the :term:`__name__` by which we find the resource. """ url_safer = unicodedata.normalize('NFKD', title).encode('ascii', 'ignore') url_safe = re.sub('[^\w\s-]', '', native_(url_safer, encoding="utf8")).strip().lower() return re.sub('[-\s]+', '-', url_safe)
def test_subpath_path_info_and_script_name_have_utf8(self): encoded = native_(text_(b"La Pe\xc3\xb1a")) decoded = text_(bytes_(encoded), "utf-8") request = DummyRequest({"PATH_INFO": "/" + encoded, "SCRIPT_NAME": "/" + encoded}) request.subpath = (decoded,) response = self._callFUT(request, "app") self.assertTrue(request.copied) self.assertEqual(response, "app") self.assertEqual(request.environ["SCRIPT_NAME"], "/" + encoded) self.assertEqual(request.environ["PATH_INFO"], "/" + encoded)
def serialize(data, secret): import hmac import base64 from hashlib import sha1 from pyramid.compat import bytes_ from pyramid.compat import native_ from pyramid.compat import pickle pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret, 'utf-8'), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def test_post_collection_no_id(self): req_body = b'"text","number"\n"abc",2\n' res = self.app.post("%s" % self.path, params=req_body, content_type=CsvMime.mime_type_string, status=201) self.assert_is_not_none(res) self.assert_true(res.headers['Location'].endswith(self.path)) self.assert_not_equal(native_(res.body).split(os.linesep)[1][:2], '""')
def test_handler_w_unicode_unauthenticated_userid(self): from pyramid.compat import native_ from pyramid_tm.compat import PY3 USERID = b'phred/\xd1\x80\xd0\xb5\xd1\x81'.decode('utf-8') self.config.testing_securitypolicy(userid=USERID) self._callFUT() if PY3: # pragma: no cover Py3k self.assertEqual(self.txn.username, ' phred/рес') else: self.assertEqual(self.txn.username, ' ' + native_(USERID, 'utf-8'))
def test_subpath_path_info_and_script_name_have_utf8(self): encoded = native_(text_(b'La Pe\xc3\xb1a')) decoded = text_(bytes_(encoded), 'utf-8') request = DummyRequest({'PATH_INFO':'/' + encoded, 'SCRIPT_NAME':'/' + encoded}) request.subpath = (decoded, ) response = self._callFUT(request, 'app') self.assertTrue(request.copied) self.assertEqual(response, 'app') self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded) self.assertEqual(request.environ['PATH_INFO'], '/' + encoded)
def call_app_with_subpath_as_path_info(request, app): # Copy the request. Use the source request's subpath (if it exists) as # the new request's PATH_INFO. Set the request copy's SCRIPT_NAME to the # prefix before the subpath. Call the application with the new request # and return a response. # # Postconditions: # - SCRIPT_NAME and PATH_INFO are empty or start with / # - At least one of SCRIPT_NAME or PATH_INFO are set. # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should # be '/'). environ = request.environ script_name = environ.get('SCRIPT_NAME', '') path_info = environ.get('PATH_INFO', '/') subpath = list(getattr(request, 'subpath', ())) new_script_name = '' # compute new_path_info new_path_info = '/' + '/'.join( [native_(x.encode('utf-8'), 'latin-1') for x in subpath]) if new_path_info != '/': # don't want a sole double-slash if path_info != '/': # if orig path_info is '/', we're already done if path_info.endswith('/'): # readd trailing slash stripped by subpath (traversal) # conversion new_path_info += '/' # compute new_script_name workback = (script_name + path_info).split('/') tmp = [] while workback: if tmp == subpath: break el = workback.pop() if el: tmp.insert(0, text_(bytes_(el, 'latin-1'), 'utf-8')) # strip all trailing slashes from workback to avoid appending undue slashes # to end of script_name while workback and (workback[-1] == ''): workback = workback[:-1] new_script_name = '/'.join(workback) new_request = request.copy() new_request.environ['SCRIPT_NAME'] = new_script_name new_request.environ['PATH_INFO'] = new_path_info return new_request.get_response(app)
def pre(self, command, output_dir, vars): if vars['package'] == 'site': raise ValueError('Sorry, you may not name your package "site". ' 'The package name "site" has a special meaning in ' 'Python. Please name it anything except "site".') vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars)
def call_app_with_subpath_as_path_info(request, app): # Copy the request. Use the source request's subpath (if it exists) as # the new request's PATH_INFO. Set the request copy's SCRIPT_NAME to the # prefix before the subpath. Call the application with the new request # and return a response. # # Postconditions: # - SCRIPT_NAME and PATH_INFO are empty or start with / # - At least one of SCRIPT_NAME or PATH_INFO are set. # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should # be '/'). environ = request.environ script_name = environ.get('SCRIPT_NAME', '') path_info = environ.get('PATH_INFO', '/') subpath = list(getattr(request, 'subpath', ())) new_script_name = '' # compute new_path_info new_path_info = '/' + '/'.join([native_(x.encode('utf-8'), 'latin-1') for x in subpath]) if new_path_info != '/': # don't want a sole double-slash if path_info != '/': # if orig path_info is '/', we're already done if path_info.endswith('/'): # readd trailing slash stripped by subpath (traversal) # conversion new_path_info += '/' # compute new_script_name workback = (script_name + path_info).split('/') tmp = [] while workback: if tmp == subpath: break el = workback.pop() if el: tmp.insert(0, text_(bytes_(el, 'latin-1'), 'utf-8')) # strip all trailing slashes from workback to avoid appending undue slashes # to end of script_name while workback and (workback[-1] == ''): workback = workback[:-1] new_script_name = '/'.join(workback) new_request = request.copy() new_request.environ['SCRIPT_NAME'] = new_script_name new_request.environ['PATH_INFO'] = new_path_info return new_request.get_response(app)
def test_route_url_with_anchor_binary(self): from pyramid.interfaces import IRoutesMapper request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) result = request.route_url('flub', _anchor=b"La Pe\xc3\xb1a") self.assertEqual( result, native_( text_(b'http://example.com:5432/1/2/3#La Pe\xc3\xb1a', 'utf-8'), 'utf-8'))
def create_version_hash(uri_dict, request): ''' Turn a response into a version hash. :param dict uri_dict: :param pyramid.request.Request request: :rtype: str ''' json = render(renderer_name='json', value=uri_dict, request=request) hash = native_( base64.b64encode(hashlib.md5(bytes_(json, encoding='UTF-8')).digest()), encoding='UTF-8') return hash.strip('=')
def test_post_collection_no_id(self, view_app_creator): # pylint:disable=W0621 # This only works in the memory backend because of the referential # constraint of the parent attribute. req_body = b'"text","number"\n"abc",2\n' res = view_app_creator.post("%s" % self.path, params=req_body, content_type=CsvMime.mime_type_string, status=201) assert not res is None assert res.headers['Location'].endswith(self.path) assert native_(res.body).split(os.linesep)[1][:2] != '""'
def serialize(data, secret): from pyramid.compat import ( pickle, bytes_, native_ ) from hashlib import sha1 import hmac import base64 pickled = pickle.dumps('123', pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def __record_message(self, logging_level, message): do_record = True if logging_level >= logging.ERROR: do_record = not self._disable_err_warn_rec if do_record: self._error_count += 1 self.abort_execution = True if do_record: msg = "%s - %s" % (self.__name, native_(message)) evt = (logging_level, msg) # pylint:disable=W0212 self._root_recorder._message_stack.append(evt) self._root_recorder._logger.log(*evt)
def get_callable_name(name): """ Verifies that the ``name`` is ascii and will raise a ``ConfigurationError`` if it is not. """ try: return native_(name, 'ascii') except (UnicodeEncodeError, UnicodeDecodeError): msg = ( '`name="%s"` is invalid. `name` must be ascii because it is ' 'used on __name__ of the method' ) raise ConfigurationError(msg % name)
def generator(dict): newdict = {} for k, v in dict.items(): if v.__class__ is text_type: v = native_(v, "utf-8") if k == star and is_nonstr_iter(v): v = "/".join([quote_path_segment(x) for x in v]) elif k != star: if v.__class__ not in string_types: v = str(v) v = url_quote(v, safe="") newdict[k] = v return gen % newdict
def pre(self, command, output_dir, vars): if vars['package'] == 'site': raise ValueError( 'Sorry, you may not name your package "site". ' 'The package name "site" has a special meaning in ' 'Python. Please name it anything except "site".') vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars)
def _save_session(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False sess_val = native_( serializer.dumps((self.accessed, self.created, dict(self)))) self._plug.dumps(self, self.request, sess_val) self._cookie.set_cookies(response, self._session_id) return True
def generator(dict): newdict = {} for k, v in dict.items(): if v.__class__ is text_type: v = native_(v, 'utf-8') if k == star and is_nonstr_iter(v): v = '/'.join([quote_path_segment(x) for x in v]) elif k != star: if v.__class__ not in string_types: v = str(v) v = url_quote(v, safe='') newdict[k] = v return gen % newdict
def test_matcher_functional_oldstyle(self): self.matches('/:x', '', None) self.matches('/:x', '/', None) self.matches('/abc/:def', '/abc/', None) self.matches('/:x', '/a', {'x':'a'}) self.matches('zzz/:x', '/zzz/abc', {'x':'abc'}) self.matches('zzz/:x*traverse', '/zzz/abc', {'x':'abc', 'traverse':()}) self.matches('zzz/:x*traverse', '/zzz/abc/def/g', {'x':'abc', 'traverse':('def', 'g')}) self.matches('*traverse', '/zzz/abc', {'traverse':('zzz', 'abc')}) self.matches('*traverse', '/zzz/ abc', {'traverse':('zzz', ' abc')}) #'/La%20Pe%C3%B1a' self.matches(':x', native_(b'/La Pe\xc3\xb1a'), {'x':text_(b'La Pe\xf1a')}) # '/La%20Pe%C3%B1a/x' self.matches('*traverse', native_(b'/La Pe\xc3\xb1a/x'), {'traverse':(text_(b'La Pe\xf1a'), 'x')}) self.matches('/foo/:id.html', '/foo/bar.html', {'id':'bar'}) self.matches('/foo/:id_html', '/foo/bar_html', {'id_html':'bar_html'}) self.matches('zzz/:_', '/zzz/abc', {'_':'abc'}) self.matches('zzz/:_abc', '/zzz/abc', {'_abc':'abc'}) self.matches('zzz/:abc_def', '/zzz/abc', {'abc_def':'abc'})
def test_get_collection_with_refs_options(self, view_app_creator): # pylint:disable=W0621 # The refs options are not processed by the renderers, so we need # a native everest view with a defined response MIME type. view_app_creator.config.add_resource_view( IMyEntity, default_response_content_type=CsvMime, request_method=RequestMethods.GET) res1 = view_app_creator.get(self.path, params=dict(refs='parent:OFF'), status=200) assert not res1 is None assert native_(res1.body).find(',"parent",') == -1 assert native_(res1.body).find(',"parent.id",') == -1 res2 = view_app_creator.get(self.path, params=dict(refs='parent:INLINE'), status=200) assert not res2 is None assert native_(res2.body).find(',"parent",') == -1 assert native_(res2.body).find(',"parent.id",') != -1 # Bogus refs parameters. view_app_creator.get(self.path, params=dict(refs='parent:XXX'), status=500)
def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() uc = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.resource_url(context, anchor=uc) self.assertEqual( result, native_( text_(b'http://example.com/context/#La Pe\xc3\xb1a', 'utf-8'), 'utf-8') )
def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): request = self._makeOne() self._registerResourceURL(request.registry) context = DummyContext() uc = text_(b'La Pe\xc3\xb1a', 'utf-8') result = request.resource_url(context, anchor=uc) self.assertEqual( result, native_( text_(b'http://example.com:5432/context/#La Pe\xc3\xb1a', 'utf-8'), 'utf-8') )
def test_subpath_path_info_and_script_name_have_utf8(self): encoded = native_(text_(b'La Pe\xc3\xb1a')) decoded = text_(bytes_(encoded), 'utf-8') request = DummyRequest({ 'PATH_INFO': '/' + encoded, 'SCRIPT_NAME': '/' + encoded }) request.subpath = (decoded, ) response = self._callFUT(request, 'app') self.assertTrue(request.copied) self.assertEqual(response, 'app') self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded) self.assertEqual(request.environ['PATH_INFO'], '/' + encoded)
def pre(self, command, output_dir, vars): """ Overrides :meth:`pyramid.scaffolds.template.Template.pre`, adding several variables to the default variables list (including ``random_string``, and ``package_logger``). It also prevents common misnamings (such as naming a package "site" or naming a package logger "root". """ vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars)
def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) sig = hmac.new(bytes_(secret), pickled, sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def quote_path_segment(segment, safe=''): """ %s """ % quote_path_segment_doc # The bit of this code that deals with ``_segment_cache`` is an # optimization: we cache all the computation of URL path segments # in this module-scope dictionary with the original string (or # unicode value) as the key, so we can look it up later without # needing to reencode or re-url-quote it try: return _segment_cache[(segment, safe)] except KeyError: if segment.__class__ not in (text_type, binary_type): segment = str(segment) result = url_quote(native_(segment, 'utf-8'), safe) # we don't need a lock to mutate _segment_cache, as the below # will generate exactly one Python bytecode (STORE_SUBSCR) _segment_cache[(segment, safe)] = result return result
def render_template(self, content, vars, filename=None): """ Return a bytestring representing a templated file based on the input (content) and the variable names defined (vars). ``filename`` is used for exception reporting.""" # this method must not be named "template_renderer" fbo of extension # scaffolds that need to work under pyramid 1.2 and 1.3, and which # need to do "template_renderer = # staticmethod(paste_script_template_renderer)" content = native_(content, fsenc) try: return bytes_( substitute_escaped_double_braces( substitute_double_braces(content, TypeMapper(vars))), fsenc) except Exception as e: _add_except(e, ' in file %s' % filename) raise
def pre(self, command, output_dir, vars): """ Overrides :meth:`pyramid.scaffolds.template.Template.pre`, adding several variables to the default variables list (including ``random_string``, and ``package_logger``). It also prevents common misnamings (such as naming a package "site" or naming a package logger "root". """ if vars['package'] == 'site': raise ValueError('Sorry, you may not name your package "site". ' 'The package name "site" has a special meaning in ' 'Python. Please name it anything except "site".') vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' package_logger = 'app' vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars)
def signed_serialize(data, secret): """ Serialize any pickleable structure (``data``) and sign it using the ``secret`` (must be a string). Return the serialization, which includes the signature as its first 40 bytes. The ``signed_deserialize`` method will deserialize such a value. This function is useful for creating signed cookies. For example: .. code-block:: python cookieval = signed_serialize({'a':1}, 'secret') response.set_cookie('signed_cookie', cookieval) """ pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) try: # bw-compat with pyramid <= 1.5b1 where latin1 is the default secret = bytes_(secret) except UnicodeEncodeError: secret = bytes_(secret, "utf-8") sig = hmac.new(secret, pickled, hashlib.sha1).hexdigest() return sig + native_(base64.b64encode(pickled))
def embed(request, *elements, **kw): """ as_user=True for current user """ # Should really be more careful about what gets included instead. # Cache cut response time from ~800ms to ~420ms. as_user = kw.get('as_user') path = join(*elements) path = unquote_bytes_to_wsgi(native_(path)) if as_user is not None: result, embedded, linked = _embed(request, path, as_user) else: cached = embed_cache.get(path, None) if cached is None: cached = _embed(request, path) embed_cache[path] = cached result, embedded, linked = cached result = deepcopy(result) request._embedded_uuids.update(embedded) request._linked_uuids.update(linked) return result
def _set_cookie(self, response): if not self._cookie_on_exception: exception = getattr(self.request, 'exception', None) if exception is not None: # dont set a cookie during exceptions return False cookieval = native_( serializer.dumps((self.accessed, self.created, dict(self)))) if len(cookieval) > 4064: raise ValueError( 'Cookie value is too long to store (%s bytes)' % len(cookieval)) response.set_cookie( self._cookie_name, value=cookieval, max_age=self._cookie_max_age, path=self._cookie_path, domain=self._cookie_domain, secure=self._cookie_secure, httponly=self._cookie_httponly, ) return True
def test_me(self): # /api/me.json # Test unauthenticated/unauthorized access headers = {'Accept': 'application/json'} res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertEqual(res.json, {'data': None}) # Test unauthenticated access -- invalid scheme headers = {'Accept': 'application/json', 'Authorization': 'Test Test'} res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertEqual(res.json, {'data': None}) # Test unauthenticated access -- missing token headers = {'Accept': 'application/json', 'Authorization': 'Token'} res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertEqual(res.json, {'data': None}) # Test unauthenticated access -- mangled traditional token headers = {'Accept': 'application/json', 'Authorization': 'Token +='} res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertEqual(res.json, {'data': None}) token = b64encode('%s%s' % (self.user_user.email, self.user_user.api_token)) headers = { 'Accept': 'application/json', 'Authorization': 'Token %s' % native_(token) } res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertEqual(res.json, {'data': None}) # Test authenticated/authorized access token = self.user_user.authorization_token headers = { 'Accept': 'application/json', 'Authorization': 'Token %s' % token } res = self.testapp.get('/api/me.json', headers=headers, status=200) self.assertIn('data', res.json) self.assertIn('email', res.json['data']) self.assertEqual(res.json['data']['email'], '*****@*****.**')
def parse_ticket(secret, ticket, ip, hashalg='md5'): """ Parse the ticket, returning (timestamp, userid, tokens, user_data). If the ticket cannot be parsed, a ``BadTicket`` exception will be raised with an explanation. """ ticket = native_(ticket).strip('"') digest_size = hashlib.new(hashalg).digest_size * 2 digest = ticket[:digest_size] try: timestamp = int(ticket[digest_size:digest_size + 8], 16) except ValueError as e: raise BadTicket('Timestamp is not a hex integer: %s' % e) try: userid, data = ticket[digest_size + 8:].split('!', 1) except ValueError: raise BadTicket('userid is not followed by !') userid = url_unquote(userid) if '!' in data: tokens, user_data = data.split('!', 1) else: # pragma: no cover (never generated) # @@: Is this the right order? tokens = '' user_data = data expected = calculate_digest(ip, timestamp, secret, userid, tokens, user_data, hashalg) # Avoid timing attacks (see # http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf) if strings_differ(expected, digest): raise BadTicket('Digest signature is not correct', expected=(expected, digest)) tokens = tokens.split(',') return (timestamp, userid, tokens, user_data)
def matcher(path): # This function really wants to consume Unicode patterns natively, # but if someone passes us a bytestring, we allow it by converting it # to Unicode using the ASCII decoding. We decode it using ASCII # because we don't want to accept bytestrings with high-order # characters in them here as we have no idea what the encoding # represents. if path.__class__ is not text_type: path = text_(path, 'ascii') m = match(path) if m is None: return None d = {} for k, v in m.groupdict().items(): # k and v will be Unicode 2.6.4 and lower doesnt accept unicode # kwargs as **kw, so we explicitly cast the keys to native # strings in case someone wants to pass the result as **kw nk = native_(k, 'ascii') if k == remainder: d[nk] = split_path_info(v) else: d[nk] = v return d
def loads(self, bstruct): return native_(bstruct)
def test_it_native(self): la = native_(b'La/Pe\xc3\xb1a', 'utf-8') result = self._callFUT(la) self.assertEqual(result, 'La%2FPe%C3%B1a')
def resource_url(self, resource, *elements, **kw): """ Generate a string representing the absolute URL of the :term:`resource` object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any ``SCRIPT_NAME``. The overall result of this method is always a UTF-8 encoded string (never Unicode). Examples:: request.resource_url(resource) => http://example.com/ request.resource_url(resource, 'a.html') => http://example.com/a.html request.resource_url(resource, 'a.html', query={'q':'1'}) => http://example.com/a.html?q=1 request.resource_url(resource, 'a.html', anchor='abc') => http://example.com/a.html#abc Any positional arguments passed in as ``elements`` must be strings Unicode objects, or integer objects. These will be joined by slashes and appended to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. If any element is an integer, it will be converted to its string representation before being URL-quoted. .. warning:: if no ``elements`` arguments are specified, the resource URL will end with a trailing slash. If any ``elements`` are used, the generated URL will *not* end in trailing a slash. If a keyword argument ``query`` is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of ``query`` must be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of ``pyramid.url.urlencode`` function. After the query data is turned into a query string, a leading ``?`` is prepended, and the resulting string is appended to the generated URL. .. note:: Python data structures that are passed as ``query`` which are sequences or dictionaries are turned into a string under the same rules as when run through :func:`urllib.urlencode` with the ``doseq`` argument equal to ``True``. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument ``anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is ``http://example.com/resource/url``, the resulting generated URL will be ``http://example.com/resource/url#foo``). .. note:: If ``anchor`` is passed as a string, it should be UTF-8 encoded. If ``anchor`` is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both ``anchor`` and ``query`` are specified, the anchor element will always follow the query element, e.g. ``http://example.com?foo=1#bar``. If the ``resource`` passed in has a ``__resource_url__`` method, it will be used to generate the URL (scheme, host, port, path) that for the base resource which is operated upon by this function. See also :ref:`overriding_resource_url_generation`. .. note:: If the :term:`resource` used is the result of a :term:`traversal`, it must be :term:`location`-aware. The resource can also be the context of a :term:`URL dispatch`; contexts found this way do not need to be location-aware. .. note:: If a 'virtual root path' is present in the request environment (the value of the WSGI environ key ``HTTP_X_VHM_ROOT``), and the resource was obtained via :term:`traversal`, the URL path will not include the virtual root prefix (it will be stripped off the left hand side of the generated URL). .. note:: For backwards compatibility purposes, this method is also aliased as the ``model_url`` method of request. """ try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c context_url = reg.queryMultiAdapter((resource, self), IContextURL) if context_url is None: context_url = TraversalContextURL(resource, self) resource_url = context_url() qs = '' anchor = '' if 'query' in kw: qs = '?' + urlencode(kw['query'], doseq=True) if 'anchor' in kw: anchor = kw['anchor'] if isinstance(anchor, text_type): anchor = native_(anchor, 'utf-8') anchor = '#' + anchor if elements: suffix = _join_elements(elements) else: suffix = '' return resource_url + suffix + qs + anchor
def test_highorder(self): la = b'La Pe\xc3\xb1a' latin1 = native_(la) result = self._callFUT(latin1) self.assertEqual(result, (text_(la, 'utf-8'), ))
def test_highorder_undecodeable(self): from pyramid.exceptions import URLDecodeError la = text_(b'La Pe\xc3\xb1a', 'utf-8') notlatin1 = native_(la) self.assertRaises(URLDecodeError, self._callFUT, notlatin1)
def _compile_route(route): # This function really wants to consume Unicode patterns natively, but if # someone passes us a bytestring, we allow it by converting it to Unicode # using the ASCII decoding. We decode it using ASCII because we don't # want to accept bytestrings with high-order characters in them here as # we have no idea what the encoding represents. if route.__class__ is not text_type: try: route = text_(route, 'ascii') except UnicodeDecodeError: raise ValueError( 'The pattern value passed to add_route must be ' 'either a Unicode string or a plain string without ' 'any non-ASCII characters (you provided %r).' % route) if old_route_re.search(route) and not route_re.search(route): route = old_route_re.sub(update_pattern, route) if not route.startswith('/'): route = '/' + route remainder = None if star_at_end.search(route): route, remainder = route.rsplit('*', 1) pat = route_re.split(route) # every element in "pat" will be Unicode (regardless of whether the # route_re regex pattern is itself Unicode or str) pat.reverse() rpat = [] gen = [] prefix = pat.pop() # invar: always at least one element (route='/'+route) # We want to generate URL-encoded URLs, so we url-quote the prefix, being # careful not to quote any embedded slashes. We have to replace '%' with # '%%' afterwards, as the strings that go into "gen" are used as string # replacement targets. gen.append(quote_path_segment(prefix, safe='/').replace('%', '%%')) # native rpat.append(re.escape(prefix)) # unicode while pat: name = pat.pop() # unicode name = name[1:-1] if ':' in name: # reg may contain colons as well, # so we must strictly split name into two parts name, reg = name.split(':', 1) else: reg = '[^/]+' gen.append('%%(%s)s' % native_(name)) # native name = '(?P<%s>%s)' % (name, reg) # unicode rpat.append(name) s = pat.pop() # unicode if s: rpat.append(re.escape(s)) # unicode # We want to generate URL-encoded URLs, so we url-quote this # literal in the pattern, being careful not to quote the embedded # slashes. We have to replace '%' with '%%' afterwards, as the # strings that go into "gen" are used as string replacement # targets. What is appended to gen is a native string. gen.append(quote_path_segment(s, safe='/').replace('%', '%%')) if remainder: rpat.append('(?P<%s>.*?)' % remainder) # unicode gen.append('%%(%s)s' % native_(remainder)) # native pattern = ''.join(rpat) + '$' # unicode match = re.compile(pattern).match def matcher(path): # This function really wants to consume Unicode patterns natively, # but if someone passes us a bytestring, we allow it by converting it # to Unicode using the ASCII decoding. We decode it using ASCII # because we don't want to accept bytestrings with high-order # characters in them here as we have no idea what the encoding # represents. if path.__class__ is not text_type: path = text_(path, 'ascii') m = match(path) if m is None: return None d = {} for k, v in m.groupdict().items(): # k and v will be Unicode 2.6.4 and lower doesnt accept unicode # kwargs as **kw, so we explicitly cast the keys to native # strings in case someone wants to pass the result as **kw nk = native_(k, 'ascii') if k == remainder: d[nk] = split_path_info(v) else: d[nk] = v return d gen = ''.join(gen) def generator(dict): newdict = {} for k, v in dict.items(): if PY3: # pragma: no cover if v.__class__ is binary_type: # url_quote below needs a native string, not bytes on Py3 v = v.decode('utf-8') else: if v.__class__ is text_type: # url_quote below needs bytes, not unicode on Py2 v = v.encode('utf-8') if k == remainder: # a stararg argument if is_nonstr_iter(v): v = '/'.join([quote_path_segment(x, safe='/') for x in v]) # native else: if v.__class__ not in string_types: v = str(v) v = quote_path_segment(v, safe='/') else: if v.__class__ not in string_types: v = str(v) # v may be bytes (py2) or native string (py3) v = quote_path_segment(v, safe='/') # at this point, the value will be a native string newdict[k] = v result = gen % newdict # native string result return result return matcher, generator
def route_url(self, route_name, *elements, **kw): """Generates a fully qualified URL for a named :app:`Pyramid` :term:`route configuration`. Use the route's ``name`` as the first positional argument. Additional positional arguments (``*elements``) are appended to the URL as path segments after it is generated. Use keyword arguments to supply values which match any dynamic path elements in the route definition. Raises a :exc:`KeyError` exception if the URL cannot be generated for any reason (not enough arguments, for example). For example, if you've defined a route named "foobar" with the path ``{foo}/{bar}/*traverse``:: request.route_url('foobar', foo='1') => <KeyError exception> request.route_url('foobar', foo='1', bar='2') => <KeyError exception> request.route_url('foobar', foo='1', bar='2', traverse=('a','b')) => http://e.com/1/2/a/b request.route_url('foobar', foo='1', bar='2', traverse='/a/b') => http://e.com/1/2/a/b Values replacing ``:segment`` arguments can be passed as strings or Unicode objects. They will be encoded to UTF-8 and URL-quoted before being placed into the generated URL. Values replacing ``*remainder`` arguments can be passed as strings *or* tuples of Unicode/string values. If a tuple is passed as a ``*remainder`` replacement value, its values are URL-quoted and encoded to UTF-8. The resulting strings are joined with slashes and rendered into the URL. If a string is passed as a ``*remainder`` replacement value, it is tacked on to the URL after being URL-quoted-except-for-embedded-slashes. If a keyword argument ``_query`` is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of ``_query`` must be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of :func:`pyramid.encode.urlencode` function. After the query data is turned into a query string, a leading ``?`` is prepended, and the resulting string is appended to the generated URL. .. note:: Python data structures that are passed as ``_query`` which are sequences or dictionaries are turned into a string under the same rules as when run through :func:`urllib.urlencode` with the ``doseq`` argument equal to ``True``. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument ``_anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is ``http://example.com/route/url``, the resulting generated URL will be ``http://example.com/route/url#foo``). .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 encoded. If ``_anchor`` is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both ``_anchor`` and ``_query`` are specified, the anchor element will always follow the query element, e.g. ``http://example.com?foo=1#bar``. If any of the keyword arguments ``_scheme``, ``_host``, or ``_port`` is passed and is non-``None``, the provided value will replace the named portion in the generated URL. For example, if you pass ``_host='foo.com'``, and the URL that would have been generated without the host replacement is ``http://example.com/a``, the result will be ``https://foo.com/a``. Note that if ``_scheme`` is passed as ``https``, and ``_port`` is not passed, the ``_port`` value is assumed to have been passed as ``443``. Likewise, if ``_scheme`` is passed as ``http`` and ``_port`` is not passed, the ``_port`` value is assumed to have been passed as ``80``. To avoid this behavior, always explicitly pass ``_port`` whenever you pass ``_scheme``. If a keyword ``_app_url`` is present, it will be used as the protocol/hostname/port/leading path prefix of the generated URL. For example, using an ``_app_url`` of ``http://example.com:8080/foo`` would cause the URL ``http://example.com:8080/foo/fleeb/flub`` to be returned from this function if the expansion of the route pattern associated with the ``route_name`` expanded to ``/fleeb/flub``. If ``_app_url`` is not specified, the result of ``request.application_url`` will be used as the prefix (the default). If both ``_app_url`` and any of ``_scheme``, ``_host``, or ``_port`` are passed, ``_app_url`` takes precedence and any values passed for ``_scheme``, ``_host``, and ``_port`` will be ignored. This function raises a :exc:`KeyError` if the URL cannot be generated due to missing replacement names. Extra replacement names are ignored. If the route object which matches the ``route_name`` argument has a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments passed to this function might be augmented or changed. """ try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c mapper = reg.getUtility(IRoutesMapper) route = mapper.get_route(route_name) if route is None: raise KeyError('No such route named %s' % route_name) if route.pregenerator is not None: elements, kw = route.pregenerator(self, elements, kw) anchor = '' qs = '' app_url = None host = None scheme = None port = None if '_query' in kw: query = kw.pop('_query') if query: qs = '?' + urlencode(query, doseq=True) if '_anchor' in kw: anchor = kw.pop('_anchor') anchor = native_(anchor, 'utf-8') anchor = '#' + anchor if '_app_url' in kw: app_url = kw.pop('_app_url') if '_host' in kw: host = kw.pop('_host') if '_scheme' in kw: scheme = kw.pop('_scheme') if '_port' in kw: port = kw.pop('_port') if app_url is None: if (scheme is not None or host is not None or port is not None): app_url = self._partial_application_url(scheme, host, port) else: app_url = self.application_url path = route.generate(kw) # raises KeyError if generate fails if elements: suffix = _join_elements(elements) if not path.endswith('/'): suffix = '/' + suffix else: suffix = '' return app_url + path + suffix + qs + anchor
def resource_url(self, resource, *elements, **kw): """ Generate a string representing the absolute URL of the :term:`resource` object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any ``SCRIPT_NAME``. The overall result of this method is always a UTF-8 encoded string. Examples:: request.resource_url(resource) => http://example.com/ request.resource_url(resource, 'a.html') => http://example.com/a.html request.resource_url(resource, 'a.html', query={'q':'1'}) => http://example.com/a.html?q=1 request.resource_url(resource, 'a.html', anchor='abc') => http://example.com/a.html#abc request.resource_url(resource, app_url='') => / Any positional arguments passed in as ``elements`` must be strings Unicode objects, or integer objects. These will be joined by slashes and appended to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. If any element is an integer, it will be converted to its string representation before being URL-quoted. .. warning:: if no ``elements`` arguments are specified, the resource URL will end with a trailing slash. If any ``elements`` are used, the generated URL will *not* end in trailing a slash. If a keyword argument ``query`` is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of ``query`` must be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of ``pyramid.url.urlencode`` function. After the query data is turned into a query string, a leading ``?`` is prepended, and the resulting string is appended to the generated URL. .. note:: Python data structures that are passed as ``query`` which are sequences or dictionaries are turned into a string under the same rules as when run through :func:`urllib.urlencode` with the ``doseq`` argument equal to ``True``. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument ``anchor`` is present, its string representation will be used as a named anchor in the generated URL (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is ``http://example.com/resource/url``, the resulting generated URL will be ``http://example.com/resource/url#foo``). .. note:: If ``anchor`` is passed as a string, it should be UTF-8 encoded. If ``anchor`` is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both ``anchor`` and ``query`` are specified, the anchor element will always follow the query element, e.g. ``http://example.com?foo=1#bar``. If any of the keyword arguments ``scheme``, ``host``, or ``port`` is passed and is non-``None``, the provided value will replace the named portion in the generated URL. For example, if you pass ``host='foo.com'``, and the URL that would have been generated without the host replacement is ``http://example.com/a``, the result will be ``https://foo.com/a``. If ``scheme`` is passed as ``https``, and an explicit ``port`` is not passed, the ``port`` value is assumed to have been passed as ``443``. Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not passed, the ``port`` value is assumed to have been passed as ``80``. To avoid this behavior, always explicitly pass ``port`` whenever you pass ``scheme``. If a keyword argument ``app_url`` is passed and is not ``None``, it should be a string that will be used as the port/hostname/initial path portion of the generated URL instead of the default request application URL. For example, if ``app_url='http://foo'``, then the resulting url of a resource that has a path of ``/baz/bar`` will be ``http://foo/baz/bar``. If you want to generate completely relative URLs with no leading scheme, host, port, or initial path, you can pass ``app_url=''`. Passing ``app_url=''` when the resource path is ``/baz/bar`` will return ``/baz/bar``. .. note:: ``app_url`` is new as of Pyramid 1.3. If ``app_url`` is passed and any of ``scheme``, ``port``, or ``host`` are also passed, ``app_url`` will take precedence and the values passed for ``scheme``, ``host``, and/or ``port`` will be ignored. If the ``resource`` passed in has a ``__resource_url__`` method, it will be used to generate the URL (scheme, host, port, path) that for the base resource which is operated upon by this function. See also :ref:`overriding_resource_url_generation`. .. note:: If the :term:`resource` used is the result of a :term:`traversal`, it must be :term:`location`-aware. The resource can also be the context of a :term:`URL dispatch`; contexts found this way do not need to be location-aware. .. note:: If a 'virtual root path' is present in the request environment (the value of the WSGI environ key ``HTTP_X_VHM_ROOT``), and the resource was obtained via :term:`traversal`, the URL path will not include the virtual root prefix (it will be stripped off the left hand side of the generated URL). .. note:: For backwards compatibility purposes, this method is also aliased as the ``model_url`` method of request. """ try: reg = self.registry except AttributeError: reg = get_current_registry() # b/c url_adapter = reg.queryMultiAdapter((resource, self), IResourceURL) if url_adapter is None: url_adapter = ResourceURL(resource, self) virtual_path = getattr(url_adapter, 'virtual_path', None) if virtual_path is None: # old-style IContextURL adapter (Pyramid 1.2 and previous) warnings.warn( 'Pyramid is using an IContextURL adapter to generate a ' 'resource URL; any "app_url", "host", "port", or "scheme" ' 'arguments passed to resource_url are being ignored. To ' 'avoid this behavior, as of Pyramid 1.3, register an ' 'IResourceURL adapter instead of an IContextURL ' 'adapter for the resource type(s). IContextURL adapters ' 'will be ignored in a later major release of Pyramid.', DeprecationWarning, 2) resource_url = url_adapter() else: # newer-style IResourceURL adapter (Pyramid 1.3 and after) app_url = None scheme = None host = None port = None if 'app_url' in kw: app_url = kw['app_url'] if 'scheme' in kw: scheme = kw['scheme'] if 'host' in kw: host = kw['host'] if 'port' in kw: port = kw['port'] if app_url is None: if scheme or host or port: app_url = self._partial_application_url(scheme, host, port) else: app_url = self.application_url resource_url = None local_url = getattr(resource, '__resource_url__', None) if local_url is not None: # the resource handles its own url generation d = dict( virtual_path = virtual_path, physical_path = url_adapter.physical_path, app_url = app_url, ) # allow __resource_url__ to punt by returning None resource_url = local_url(self, d) if resource_url is None: # the resource did not handle its own url generation or the # __resource_url__ function returned None resource_url = app_url + virtual_path qs = '' anchor = '' if 'query' in kw: query = kw['query'] if query: qs = '?' + urlencode(query, doseq=True) if 'anchor' in kw: anchor = kw['anchor'] if isinstance(anchor, text_type): anchor = native_(anchor, 'utf-8') anchor = '#' + anchor if elements: suffix = _join_elements(elements) else: suffix = '' return resource_url + suffix + qs + anchor
def test_url_decode_error(self): from pyramid.exceptions import URLDecodeError matcher, generator = self._callFUT('/:foo') self.assertRaises(URLDecodeError, matcher, native_(b'/\xff\xfe\x8b\x00'))