def test_build_with_query_and_query_string(): with pytest.raises(ValueError): URL.build( scheme='http', host='127.0.0.1', user='******', password='******', port=8000, path='/index.html', query=dict(arg="value1"), query_string="arg=value1", fragment="top" )
def test_build_with_query_and_query_string(): with pytest.raises(ValueError): URL.build( scheme="http", host="127.0.0.1", user="******", password="******", port=8000, path="/index.html", query=dict(arg="value1"), query_string="arg=value1", fragment="top", )
def __init__(self, path: str, *, name: Optional[str]=None) -> None: super().__init__(name=name) pattern = '' formatter = '' for part in ROUTE_RE.split(path): match = self.DYN.fullmatch(part) if match: pattern += '(?P<{}>{})'.format(match.group('var'), self.GOOD) formatter += '{' + match.group('var') + '}' continue match = self.DYN_WITH_RE.fullmatch(part) if match: pattern += '(?P<{var}>{re})'.format(**match.groupdict()) formatter += '{' + match.group('var') + '}' continue if '{' in part or '}' in part: raise ValueError("Invalid path '{}'['{}']".format(path, part)) path = URL.build(path=part).raw_path formatter += path pattern += re.escape(path) try: compiled = re.compile(pattern) except re.error as exc: raise ValueError( "Bad pattern '{}': {}".format(pattern, exc)) from None assert compiled.pattern.startswith(PATH_SEP) assert formatter.startswith('/') self._pattern = compiled self._formatter = formatter
def url_for(self, *, filename: Union[str, Path], # type: ignore append_version: Optional[bool]=None) -> URL: if append_version is None: append_version = self._append_version if isinstance(filename, Path): filename = str(filename) while filename.startswith('/'): filename = filename[1:] filename = '/' + filename # filename is not encoded url = URL.build(path=self._prefix + filename) if append_version: try: if filename.startswith('/'): filename = filename[1:] filepath = self._directory.joinpath(filename).resolve() if not self._follow_symlinks: filepath.relative_to(self._directory) except (ValueError, FileNotFoundError): # ValueError for case when path point to symlink # with follow_symlinks is False return url # relatively safe if filepath.is_file(): # TODO cache file content # with file watcher for cache invalidation with open(str(filepath), mode='rb') as f: file_bytes = f.read() h = self._get_file_hash(file_bytes) url = url.with_query({self.VERSION_KEY: h}) return url return url
def test_build_drop_dots(): u = URL.build( scheme='http', host='example.com', path='/path/../to', ) assert str(u) == 'http://example.com/to'
def _match(self, path: str) -> Optional[Dict[str, str]]: match = self._pattern.fullmatch(path) if match is None: return None else: return {key: URL.build(path=value, encoded=True).path for key, value in match.groupdict().items()}
def test_build_query_quoting(): u = URL.build(scheme="http", host="127.0.0.1", path="/файл.jpg", query="arg=Привет") assert u == URL("http://127.0.0.1/файл.jpg?arg=Привет") assert str(u) == ( "http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?" "arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82" )
def test_query_str(): u = URL.build( scheme='http', host='127.0.0.1', path='/', query_string="arg=value1" ) assert str(u) == 'http://127.0.0.1/?arg=value1'
def test_build_path_quoting(): u = URL.build( scheme='http', host='127.0.0.1', path='/файл.jpg', query=dict(arg="Привет") ) assert u == URL('http://127.0.0.1/файл.jpg?arg=Привет') assert str(u) == ('http://127.0.0.1/%D1%84%D0%B0%D0%B9%D0%BB.jpg?' 'arg=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82')
def test_build_already_encoded(): # resulting URL is invalid but not encoded u = URL.build( scheme="http", host="историк.рф", path="/путь/файл", query_string="ключ=знач", fragment="фраг", encoded=True, ) assert str(u) == "http://историк.рф/путь/файл?ключ=знач#фраг"
def test_build_with_all(): u = URL.build( scheme='http', host='127.0.0.1', user='******', password='******', port=8000, path='/index.html', query_string="arg=value1", fragment="top" ) assert str(u) == 'http://*****:*****@127.0.0.1:8000/index.html?arg=value1#top'
def test_build_with_all(): u = URL.build( scheme="http", host="127.0.0.1", user="******", password="******", port=8000, path="/index.html", query_string="arg=value1", fragment="top", ) assert str(u) == "http://*****:*****@127.0.0.1:8000/index.html?arg=value1#top"
def __init__(self, runner, sock, *, shutdown_timeout=60.0, ssl_context=None, backlog=128): super().__init__(runner, shutdown_timeout=shutdown_timeout, ssl_context=ssl_context, backlog=backlog) self._sock = sock scheme = 'https' if self._ssl_context else 'http' if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX: name = '{}://unix:{}:'.format(scheme, sock.getsockname()) else: host, port = sock.getsockname()[:2] name = str(URL.build(scheme=scheme, host=host, port=port)) self._name = name
async def resolve(self, request: Request) -> _Resolve: path = request.rel_url.raw_path method = request.method allowed_methods = set(self._routes) if not path.startswith(self._prefix): return None, set() if method not in allowed_methods: return None, allowed_methods match_dict = {'filename': URL.build(path=path[len(self._prefix)+1:], encoded=True).path} return (UrlMappingMatchInfo(match_dict, self._routes[method]), allowed_methods)
def test_build_encode(): u = URL.build( scheme="http", host="историк.рф", path="/путь/файл", query_string="ключ=знач", fragment="фраг", ) expected = ( "http://xn--h1aagokeh.xn--p1ai" "/%D0%BF%D1%83%D1%82%D1%8C/%D1%84%D0%B0%D0%B9%D0%BB" "?%D0%BA%D0%BB%D1%8E%D1%87=%D0%B7%D0%BD%D0%B0%D1%87" "#%D1%84%D1%80%D0%B0%D0%B3" ) assert str(u) == expected
def add_resource(self, path, *, name=None): if path and not path.startswith('/'): raise ValueError("path should be started with / or be empty") # Reuse last added resource if path and name are the same if self._resources: resource = self._resources[-1] if resource.name == name and resource.raw_match(path): return resource if not ('{' in path or '}' in path or ROUTE_RE.search(path)): url = URL.build(path=path) resource = PlainResource(url.raw_path, name=name) self.register_resource(resource) return resource resource = DynamicResource(path, name=name) self.register_resource(resource) return resource
def url_for( # type: ignore self, *, filename: Union[str, Path], append_version: Optional[bool] = None, ) -> URL: if append_version is None: append_version = self._append_version if isinstance(filename, Path): filename = str(filename) filename = filename.lstrip("/") url = URL.build(path=self._prefix, encoded=True) # filename is not encoded if YARL_VERSION < (1, 6): url = url / filename.replace("%", "%25") else: url = url / filename if append_version: try: filepath = self._directory.joinpath(filename).resolve() if not self._follow_symlinks: filepath.relative_to(self._directory) except (ValueError, FileNotFoundError): # ValueError for case when path point to symlink # with follow_symlinks is False return url # relatively safe if filepath.is_file(): # TODO cache file content # with file watcher for cache invalidation with filepath.open("rb") as f: file_bytes = f.read() h = self._get_file_hash(file_bytes) url = url.with_query({self.VERSION_KEY: h}) return url return url
def __init__( self, host: str, mac: str, token='', session: aiohttp.client.ClientSession = None, ): """Initialize the bulb.""" self._close_session = False self._host = host self._mac = mac self._session = session self.brightness = 0 self._color = None self._consumption = 0 self.data = None self._firmware = None self._mode = None self._bulb_type = None self._state = None self._transition_time = 0 self.uri = URL.build(scheme="http", host=self._host).join(URI_BULB) / self._mac self.token = token
def url_for( self, *, filename: Union[str, Path], # type: ignore append_version: Optional[bool] = None) -> URL: if append_version is None: append_version = self._append_version if isinstance(filename, Path): filename = str(filename) while filename.startswith('/'): filename = filename[1:] filename = '/' + filename # filename is not encoded url = URL.build(path=self._prefix + filename) if append_version: try: if filename.startswith('/'): filename = filename[1:] filepath = self._directory.joinpath(filename).resolve() if not self._follow_symlinks: filepath.relative_to(self._directory) except (ValueError, FileNotFoundError): # ValueError for case when path point to symlink # with follow_symlinks is False return url # relatively safe if filepath.is_file(): # TODO cache file content # with file watcher for cache invalidation with open(str(filepath), mode='rb') as f: file_bytes = f.read() h = self._get_file_hash(file_bytes) url = url.with_query({self.VERSION_KEY: h}) return url return url
def setup_catalog(app: web.Application, *, disable_auth=False): # ---------------------------------------------- # TODO: temporary, just to check compatibility between # trafaret and pydantic schemas cfg, _ = assert_valid_config(app) # --------------------------------------------- # resolve url app[KCATALOG_ORIGIN] = URL.build(scheme="http", host=cfg["host"], port=cfg["port"]) app[KCATALOG_VERSION_PREFIX] = cfg["version"] specs = app[APP_OPENAPI_SPECS_KEY] # validated openapi specs exclude: List[str] = [] route_def: RouteDef for route_def in catalog_api_handlers.routes: route_def.kwargs["name"] = operation_id = route_def.handler.__name__ exclude.append(operation_id) app.add_routes(catalog_api_handlers.routes) # bind the rest routes with the reverse-proxy-handler # FIXME: this would reroute **anything** to the catalog service! handler = (_reverse_proxy_handler.__wrapped__ if disable_auth else _reverse_proxy_handler) routes = [ web.route(method.upper(), path, handler, name=operation_id) for method, path, operation_id, tags in iter_path_operations(specs) if "catalog" in tags and operation_id not in exclude ] assert routes, "Got no paths tagged as catalog" # nosec # reverse proxy to catalog's API app.router.add_routes(routes)
def modify_request(self, request: web.Request) -> None: """ Apply common path conventions eg. / > /index.html, /foobar > /foobar.html """ filename = URL.build(path=request.match_info['filename'], encoded=True).path raw_path = self._directory.joinpath(filename) try: filepath = raw_path.resolve(strict=True) except FileNotFoundError: try: html_file = raw_path.with_name(raw_path.name + '.html').resolve().relative_to(self._directory) except (FileNotFoundError, ValueError): pass else: request.match_info['filename'] = str(html_file) else: if filepath.is_dir(): index_file = filepath / 'index.html' if index_file.exists(): try: request.match_info['filename'] = str(index_file.relative_to(self._directory)) except ValueError: # path is not not relative to self._directory pass
def coerce_url(url: Union[URL, str], https: bool = False) -> URL: """ Coerce URL to valid format :param url: URL :param https: Force https if no scheme in url :return: str """ if isinstance(url, str): url = URL(url.strip()) scheme = "https" if https else "http" if not url.is_absolute(): url_string = str(url) split = url_string.split("/", 1) url = URL.build(scheme=scheme, host=split[0]) if len(split) > 1: url = url.with_path(split[1]) if (url.scheme == "http" and https) or not url.scheme: url = url.with_scheme(scheme) return url
async def _request( self, uri: str = "", *, data: Optional[Any] = None, method: str = "GET", no_agreement: bool = False, ) -> Any: """Handle a request to the Quby ToonAPI.""" if self.token_refresh_method is not None: self.token = await self.token_refresh_method() if self._status is None and self.agreement_id and not no_agreement: self.activate_agreement(agreement_id=self.agreement_id, ) url = URL.build( scheme=TOON_API_SCHEME, host=TOON_API_HOST, port=TOON_API_PORT, path=TOON_API_BASE_PATH, ).join(URL(uri)) headers = { "Authorization": f"Bearer {self.token}", "User-Agent": self.user_agent, "Accept": "application/json", } if not no_agreement and self._status is not None: headers.update({ "X-Common-Name": self._status.agreement.display_common_name, "X-Agreement-ID": self._status.agreement.agreement_id, }) if self._session is None: self._session = aiohttp.ClientSession() self._close_session = True try: with async_timeout.timeout(self.request_timeout): response = await self._session.request( method, url, json=data, headers=headers, ssl=True, ) except asyncio.TimeoutError as exception: raise ToonConnectionTimeoutError( "Timeout occurred while connecting to the Quby ToonAPI" ) from exception except (aiohttp.ClientError, socket.gaierror) as exception: raise ToonConnectionError( "Error occurred while communicating with the Quby ToonAPI" ) from exception content_type = response.headers.get("Content-Type", "") # Error handling if (response.status // 100) in [4, 5]: contents = await response.read() response.close() if response.status == 429: raise ToonRateLimitError( "Rate limit error has occurred with the Quby ToonAPI") if content_type == "application/json": raise ToonError(response.status, json.loads(contents.decode("utf8"))) raise ToonError(response.status, {"message": contents.decode("utf8")}) # Handle empty response if response.status == 204: return if "application/json" in content_type: return await response.json() return await response.text()
def build_amqp_url( user: str = None, password: str = None, host: str = None, port: int = None, virtual_host: str = None, connection_attempts: int = None, heartbeat_interval: int = None, ssl_options: dict = None, ) -> str: """ Create a AMQP connection URL from parameters. If no parameters are passed to optional arguments then the environment is inspected for settings prefixed with ``RABBITMQ_`` and then defaults values are used. :param user: Login credentials username. Default value is 'guest'. :param password: Login credentials password. Default value is 'guest'. :param host: AMQP Broker host. Default value is 127.0.0.1. :param port: AMQP Broker port. Default value is 5672. :param virtualhost: AMQP virtualhost to use. Default value is '/' :param connection_attempts: The number of connections attempts. No default value. :param heartbeat_interval: The interval to use between heartbeat requests. No default value. :param ssl_options: A dict of public ssl context-related values for the SSL connection. Available keys are: - ca_certs as a string containing path to ca certificate file - cert_reqs - certfile - keyfile ia a string containing the path to key file - ssl_version """ options = {} if connection_attempts: options["connection_attempts"] = connection_attempts if heartbeat_interval: options["heartbeat_interval"] = heartbeat_interval if ssl_options: # Convert any ssl enumerations to a scalar value prior to forming # the url. for k, v in ssl_options.items(): if isinstance(v, enum.Enum): ssl_options[k] = v.value options.update(ssl_options) default_port = 5671 if ssl_options else 5672 url = URL.build( scheme="amqps" if ssl_options else "amqp", host=host if host else os.getenv("RABBITMQ_HOST", "127.0.0.1"), port=port if port else int(os.getenv("RABBITMQ_PORT", str(default_port))), user=user if user else os.getenv("RABBITMQ_USER", "guest"), password=password if password else os.getenv("RABBITMQ_PASS", "guest"), path=f"/{virtual_host}" if virtual_host else "//", query=options, ) return str(url)
def url_for(self, **parts: str) -> URL: url = self._formatter.format_map( {k: _quote_path(v) for k, v in parts.items()}) return URL.build(path=url, encoded=True)
def _quote_path(value: str) -> str: if YARL_VERSION < (1, 6): value = value.replace("%", "%25") return URL.build(path=value, encoded=False).raw_path
def name(self) -> str: scheme = "https" if self._ssl_context else "http" host = "0.0.0.0" if self._host is None else self._host return str(URL.build(scheme=scheme, host=host, port=self._port))
async def request( self, uri: str, method: str = "GET", additional_headers: dict | None = None, data: Any | None = None, json_data: dict | None = None, params: Mapping[str, str] | None = None, ) -> Any: """Handle a request to the weenect API. Make a request against the weenect API and handles the response. Args: uri: The request URI on the weenect API to call. method: HTTP method to use for the request; e.g., GET, POST. data: RAW HTTP request data to send with the request. json_data: Dictionary of data to send as JSON with the request. params: Mapping of request parameters to send with the request. Returns: The response from the API. In case the response is a JSON response, the method will return a decoded JSON response as a Python dictionary. In other cases, it will return the RAW text response. Raises: WeenectConnectionError: An error occurred while communicating with the weenect API (connection issues). WeenectHomeError: An error occurred while processing the response from the weenect API (invalid data). """ url = URL.build(scheme=SCHEME, host=API_HOST, path=API_VERSION) / uri headers = { "User-Agent": self.user_agent, "Accept": "application/json, text/plain, */*", "Origin": APP_URL, "x-app-version": "0.1.0", "x-app-user-id": "", "x-app-type": "userspace", "DNT": "1", } if self._auth_token is not None: headers.update({"Authorization": self._auth_token}) if additional_headers is not None: headers.update(additional_headers) if self._session is None: self._session = aiohttp.ClientSession() self._close_session = True try: async with async_timeout.timeout(self.request_timeout): response = await self._session.request( method, url, data=data, json=json_data, params=params, headers=headers, ) except asyncio.TimeoutError as exception: raise WeenectConnectionError( "Timeout occurred while connecting to the weenect API." ) from exception except (aiohttp.ClientError, socket.gaierror) as exception: raise WeenectConnectionError( "Error occurred while communicating with the weenect API." ) from exception content_type = response.headers.get("Content-Type", "") if response.status // 100 in [4, 5]: contents = await response.read() response.close() if (response.status == 401 and json.loads( contents.decode("utf8"))["error"] == "Invalid token"): self._auth_token = None return await self.authenticated_request( uri=uri, method=method, additional_headers=additional_headers, data=data, json_data=json_data, params=params, ) if content_type == "application/json": raise WeenectError(response.status, json.loads(contents.decode("utf8"))) raise WeenectError(response.status, {"message": contents.decode("utf8")}) if response.status == 204: # NO CONTENT response.close() return None if "application/json" in content_type: return await response.json() text = await response.text() return {"message": text}
def url(self) -> URL: url = URL.build(scheme=self.scheme, host=self.host) return url.join(self._rel_url)
def test_build_with_user_password(): u = URL.build(scheme='http', host='127.0.0.1', user='******', password='******') assert str(u) == 'http://*****:*****@127.0.0.1'
def url_for(self): return URL.build(path=self._path, encoded=True)
def name(self): scheme = 'https' if self._ssl_context else 'http' return str(URL.build(scheme=scheme, host=self._host, port=self._port))
def test_query_dict(): u = URL.build(scheme="http", host="127.0.0.1", path="/", query=dict(arg="value1")) assert str(u) == "http://127.0.0.1/?arg=value1"
def url(self): url = URL.build(scheme=self.scheme, host=self.host) return url.join(self._rel_url)
def url_for(self, **parts: str) -> URL: url = self._formatter.format_map( {k: URL.build(path=v).raw_path for k, v in parts.items()}) return URL.build(path=url)
def make_default_endpoint(region: str) -> URL: return URL.build(scheme="https", host=f"{SERVICE}.{region}.amazonaws.com", path="/")
def url_for(self) -> URL: # type: ignore return URL.build(path=self._path, encoded=True)
def _make_server_creators(handler, *, loop, ssl_context, host, port, path, sock, backlog): scheme = 'https' if ssl_context else 'http' base_url = URL.build(scheme=scheme, host='localhost', port=port) if path is None: paths = () elif isinstance(path, (str, bytes, bytearray, memoryview))\ or not isinstance(path, Iterable): paths = (path,) else: paths = path if sock is None: socks = () elif not isinstance(sock, Iterable): socks = (sock,) else: socks = sock if host is None: if (paths or socks) and not port: hosts = () else: hosts = ("0.0.0.0",) elif isinstance(host, (str, bytes, bytearray, memoryview))\ or not isinstance(host, Iterable): hosts = (host,) else: hosts = host if hosts and port is None: port = 8443 if ssl_context else 8080 server_creations = [] uris = [str(base_url.with_host(host).with_port(port)) for host in hosts] if hosts: # Multiple hosts bound to same server is available in most loop # implementations, but only send multiple if we have multiple. host_binding = hosts[0] if len(hosts) == 1 else hosts server_creations.append( loop.create_server( handler, host_binding, port, ssl=ssl_context, backlog=backlog ) ) for path in paths: # Most loop implementations don't support multiple paths bound in same # server, so create a server for each. server_creations.append( loop.create_unix_server( handler, path, ssl=ssl_context, backlog=backlog ) ) uris.append('{}://unix:{}:'.format(scheme, path)) # Clean up prior socket path if stale and not abstract. # CPython 3.5.3+'s event loop already does this. See # https://github.com/python/asyncio/issues/425 if path[0] not in (0, '\x00'): # pragma: no branch try: if stat.S_ISSOCK(os.stat(path).st_mode): os.remove(path) except FileNotFoundError: pass for sock in socks: server_creations.append( loop.create_server( handler, sock=sock, ssl=ssl_context, backlog=backlog ) ) if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX: uris.append('{}://unix:{}:'.format(scheme, sock.getsockname())) else: host, port = sock.getsockname()[:2] uris.append(str(base_url.with_host(host).with_port(port))) return server_creations, uris
def test_build_with_user(): u = URL.build(scheme='http', host='127.0.0.1', user='******') assert str(u) == 'http://[email protected]'
def test_build_with_port(): u = URL.build(scheme='http', host='127.0.0.1', port=8000) assert str(u) == 'http://127.0.0.1:8000'
import asyncio import json import socket from importlib import metadata from typing import Any, Mapping import aiohttp import async_timeout from yarl import URL from .exceptions import WeenectConnectionError, WeenectError from .zone_modes import ZoneNotificationMode SCHEME = "https" APP_HOST = "my.weenect.com" APP_URL = str(URL.build(scheme=SCHEME, host=APP_HOST)) API_HOST = "apiv4.weenect.com" API_VERSION = "/v4" class AioWeenect: """Main class for handling connections with weenect.""" def __init__( self, password: str, username: str, request_timeout: int = 10, session: aiohttp.client.ClientSession | None = None, user_agent: str | None = None, ) -> None: """Initialize connection with weenect.
def url_for(self, **parts: str) -> URL: url = self._formatter.format_map({k: URL.build(path=v).raw_path for k, v in parts.items()}) return URL.build(path=url)
async def _request( self, uri: str = "", data: Optional[Any] = None, params: Optional[Mapping[str, str]] = None, ) -> Any: """Handle a request to an IPP server.""" scheme = "https" if self.tls else "http" method = "POST" url = URL.build(scheme=scheme, host=self.host, port=self.port, path=self.base_path).join(URL(uri)) auth = None if self.username and self.password: auth = aiohttp.BasicAuth(self.username, self.password) headers = { "User-Agent": self.user_agent, "Content-Type": "application/ipp", "Accept": "application/ipp, text/plain, */*", } if self._session is None: self._session = aiohttp.ClientSession() self._close_session = True if isinstance(data, dict): data = encode_dict(data) try: with async_timeout.timeout(self.request_timeout): response = await self._session.request( method, url, auth=auth, data=data, params=params, headers=headers, ssl=self.verify_ssl, ) except asyncio.TimeoutError as exc: raise IPPConnectionError( "Timeout occurred while connecting to IPP server.") from exc except (aiohttp.ClientError, SocketGIAError) as exc: raise IPPConnectionError( "Error occurred while communicating with IPP server.") from exc if response.status == 426: raise IPPConnectionUpgradeRequired( "Connection upgrade required while communicating with IPP server.", {"upgrade": response.headers.get("Upgrade")}, ) if (response.status // 100) in [4, 5]: content = await response.read() response.close() raise IPPResponseError( f"HTTP {response.status}", { "content-type": response.headers.get("Content-Type"), "message": content.decode("utf8"), "status-code": response.status, }, ) return await response.read()
def __init__(self, prefix: str, *, name: Optional[str]=None) -> None: assert not prefix or prefix.startswith('/'), prefix assert prefix in ('', '/') or not prefix.endswith('/'), prefix super().__init__(name=name) self._prefix = URL.build(path=prefix).raw_path
def __init__( self, method: Union[str, bytes], url: Union["URL", str, RawURL], *, params: QueryTypes = None, headers: HeaderTypes = None, cookies: CookieTypes = None, content: ContentTypes = None, data: DataTypes = None, json: Any = None, files: FilesTypes = None, version: Union[str, HTTPVersion] = HTTPVersion.H11, timeout: Optional[float] = None, proxy: Optional[str] = None, ): # method self.method: str = (method.decode("ascii").upper() if isinstance( method, bytes) else method.upper()) # http version self.version: HTTPVersion = HTTPVersion(version) # timeout self.timeout: Optional[float] = timeout # proxy self.proxy: Optional[str] = proxy # url if isinstance(url, tuple): scheme, host, port, path = url url = URL.build( scheme=scheme.decode("ascii"), host=host.decode("ascii"), port=port, path=path.decode("ascii"), ) else: url = URL(url) if params is not None: url = url.update_query(params) self.url: URL = url # headers self.headers: CIMultiDict[str] if headers is not None: self.headers = CIMultiDict(headers) else: self.headers = CIMultiDict() # cookies self.cookies = Cookies(cookies) # body self.content: ContentTypes = content self.data: DataTypes = data self.json: Any = json self.files: Optional[List[Tuple[str, FileType]]] = None if files: self.files = [] files_ = files.items() if isinstance(files, dict) else files for name, file_info in files_: if not isinstance(file_info, tuple): self.files.append((name, (None, file_info, None))) elif len(file_info) == 2: self.files.append( (name, (file_info[0], file_info[1], None))) else: self.files.append((name, file_info)) # type: ignore
def test_build_scheme_and_host(): with pytest.raises(ValueError): URL.build(host='127.0.0.1') with pytest.raises(ValueError): URL.build(scheme='http')
def _unquote_path(value: str) -> str: return URL.build(path=value, encoded=True).path
def test_build_simple(): u = URL.build(scheme='http', host='127.0.0.1') assert str(u) == 'http://127.0.0.1'
async def connect(url: str = None, *, host: str = "localhost", port: int = 5672, login: str = "guest", password: str = "guest", virtualhost: str = "/", ssl: bool = False, loop: asyncio.AbstractEventLoop = None, ssl_options: dict = None, timeout: TimeoutType = None, connection_class: Type[ConnectionType] = Connection, client_properties: dict = None, **kwargs) -> ConnectionType: """ Make connection to the broker. Example: .. code-block:: python import aio_pika async def main(): connection = await aio_pika.connect( "amqp://*****:*****@127.0.0.1/" ) Connect to localhost with default credentials: .. code-block:: python import aio_pika async def main(): connection = await aio_pika.connect() .. note:: The available keys for ssl_options parameter are: * cert_reqs * certfile * keyfile * ssl_version For an information on what the ssl_options can be set to reference the `official Python documentation`_ . Set connection name for RabbitMQ admin panel: .. code-block:: python read_connection = await connect( client_properties={ 'connection_name': 'Read connection' } ) write_connection = await connect( client_properties={ 'connection_name': 'Write connection' } ) .. note: ``client_properties`` argument requires ``aiormq>=2.9`` URL string might be contain ssl parameters e.g. `amqps://user:pass@host//?ca_certs=ca.pem&certfile=crt.pem&keyfile=key.pem` :param client_properties: add custom client capability. :param url: RFC3986_ formatted broker address. When :class:`None` will be used keyword arguments. :param host: hostname of the broker :param port: broker port 5672 by default :param login: username string. `'guest'` by default. :param password: password string. `'guest'` by default. :param virtualhost: virtualhost parameter. `'/'` by default :param ssl: use SSL for connection. Should be used with addition kwargs. :param ssl_options: A dict of values for the SSL connection. :param timeout: connection timeout in seconds :param loop: Event loop (:func:`asyncio.get_event_loop()` when :class:`None`) :param connection_class: Factory of a new connection :param kwargs: addition parameters which will be passed to the connection. :return: :class:`aio_pika.connection.Connection` .. _RFC3986: https://goo.gl/MzgYAs .. _official Python documentation: https://goo.gl/pty9xA """ if url is None: kw = kwargs kw.update(ssl_options or {}) url = URL.build( scheme="amqps" if ssl else "amqp", host=host, port=port, user=login, password=password, # yarl >= 1.3.0 requires path beginning with slash path="/" + virtualhost, query=kw, ) connection = connection_class(url, loop=loop) await connection.connect( timeout=timeout, client_properties=client_properties, loop=loop, ) return connection
async def connect(url: str = None, *, host: str = 'localhost', port: int = 5672, login: str = 'guest', password: str = 'guest', virtualhost: str = '/', ssl: bool = False, loop: asyncio.AbstractEventLoop = None, ssl_options: dict = None, connection_class: Type[Connection] = Connection, **kwargs) -> Connection: """ Make connection to the broker. Example: .. code-block:: python import aio_pika async def main(): connection = await aio_pika.connect( "amqp://*****:*****@127.0.0.1/" ) Connect to localhost with default credentials: .. code-block:: python import aio_pika async def main(): connection = await aio_pika.connect() .. note:: The available keys for ssl_options parameter are: * cert_reqs * certfile * keyfile * ssl_version For an information on what the ssl_options can be set to reference the `official Python documentation`_ . URL string might be contain ssl parameters e.g. `amqps://user:pass@host//?ca_certs=ca.pem&certfile=crt.pem&keyfile=key.pem` :param url: RFC3986_ formatted broker address. When :class:`None` will be used keyword arguments. :param host: hostname of the broker :param port: broker port 5672 by default :param login: username string. `'guest'` by default. Provide empty string for pika.credentials.ExternalCredentials usage. :param password: password string. `'guest'` by default. :param virtualhost: virtualhost parameter. `'/'` by default :param ssl: use SSL for connection. Should be used with addition kwargs. See `pika documentation`_ for more info. :param ssl_options: A dict of values for the SSL connection. :param loop: Event loop (:func:`asyncio.get_event_loop()` when :class:`None`) :param connection_class: Factory of a new connection :param kwargs: addition parameters which will be passed to the pika connection. :return: :class:`aio_pika.connection.Connection` .. _RFC3986: https://goo.gl/MzgYAs .. _pika documentation: https://goo.gl/TdVuZ9 .. _official Python documentation: https://goo.gl/pty9xA """ if url is None: kw = kwargs kw.update(ssl_options or {}) url = URL.build( scheme='amqps' if ssl else 'amqp', host=host, port=port, user=login, password=password, # yarl >= 1.3.0 requires path beginning with slash path="/" + virtualhost, query=kw) connection = connection_class(url, loop=loop) await connection.connect() return connection
def _make_server_creators(handler, *, loop, ssl_context, host, port, path, sock, backlog): scheme = 'https' if ssl_context else 'http' base_url = URL.build(scheme=scheme, host='localhost', port=port) if path is None: paths = () elif isinstance(path, (str, bytes, bytearray, memoryview))\ or not isinstance(path, Iterable): paths = (path, ) else: paths = path if sock is None: socks = () elif not isinstance(sock, Iterable): socks = (sock, ) else: socks = sock if host is None: if (paths or socks) and not port: hosts = () else: hosts = ("0.0.0.0", ) elif isinstance(host, (str, bytes, bytearray, memoryview))\ or not isinstance(host, Iterable): hosts = (host, ) else: hosts = host if hosts and port is None: port = 8443 if ssl_context else 8080 server_creations = [] uris = [str(base_url.with_host(host).with_port(port)) for host in hosts] if hosts: # Multiple hosts bound to same server is available in most loop # implementations, but only send multiple if we have multiple. host_binding = hosts[0] if len(hosts) == 1 else hosts server_creations.append( loop.create_server(handler, host_binding, port, ssl=ssl_context, backlog=backlog)) for path in paths: # Most loop implementations don't support multiple paths bound in same # server, so create a server for each. server_creations.append( loop.create_unix_server(handler, path, ssl=ssl_context, backlog=backlog)) uris.append('{}://unix:{}:'.format(scheme, path)) # Clean up prior socket path if stale and not abstract. # CPython 3.5.3+'s event loop already does this. See # https://github.com/python/asyncio/issues/425 if path[0] not in (0, '\x00'): # pragma: no branch try: if stat.S_ISSOCK(os.stat(path).st_mode): os.remove(path) except FileNotFoundError: pass for sock in socks: server_creations.append( loop.create_server(handler, sock=sock, ssl=ssl_context, backlog=backlog)) if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX: uris.append('{}://unix:{}:'.format(scheme, sock.getsockname())) else: host, port = sock.getsockname()[:2] uris.append(str(base_url.with_host(host).with_port(port))) return server_creations, uris
async def _request( self, uri: str, *, method: str = METH_GET, params: Mapping[str, str] | None = None, ) -> dict[str, Any]: """Handle a request to the Garages Amsterdam API. Args: uri: Request URI, without '/', for example, 'status' method: HTTP method to use, for example, 'GET' params: Extra options to improve or limit the response. Returns: A Python dictionary (text) with the response from the Garages Amsterdam API. Raises: GaragesAmsterdamConnectionError: An error occurred while communicating with the Garages Amsterdam API. GaragesAmsterdamError: Received an unexpected response from the Garages Amsterdam API. """ version = metadata.version(__package__) url = URL.build(scheme="http", host="opd.it-t.nl", path="/data/amsterdam/").join(URL(uri)) headers = { "Accept": "application/json, text/plain", "User-Agent": f"PythonGaragesAmsterdam/{version}", } if self.session is None: self.session = ClientSession() self._close_session = True try: async with async_timeout.timeout(self.request_timeout): response = await self.session.request( method, url, params=params, headers=headers, ssl=False, ) response.raise_for_status() except asyncio.TimeoutError as exception: raise GaragesAmsterdamConnectionError( "Timeout occurred while connecting to the Garages Amsterdam API.", ) from exception except (ClientError, ClientResponseError) as exception: raise GaragesAmsterdamConnectionError( "Error occurred while communicating with the Garages Amsterdam API." ) from exception content_type = response.headers.get("Content-Type", "") if "text/plain" not in content_type: text = await response.text() raise GaragesAmsterdamError( "Unexpected response from the Garages Amsterdam API", { "Content-Type": content_type, "response": text }, ) return await response.text()
def url_for(self) -> URL: # type: ignore return URL.build(path=self._path, encoded=True)
def url(self): return str( URL.build(scheme=self['protocol'], host=self['host'], port=self['port'], path=self['path']))
def __init__(self, prefix: str, *, name: Optional[str] = None) -> None: assert not prefix or prefix.startswith('/'), prefix assert prefix in ('', '/') or not prefix.endswith('/'), prefix super().__init__(name=name) self._prefix = URL.build(path=prefix).raw_path
def parse_message(self, lines: List[bytes]) -> RawRequestMessage: # request line line = lines[0].decode("utf-8", "surrogateescape") try: method, path, version = line.split(None, 2) except ValueError: raise BadStatusLine(line) from None if len(path) > self.max_line_size: raise LineTooLong("Status line is too long", str(self.max_line_size), str(len(path))) path_part, _hash_separator, url_fragment = path.partition("#") path_part, _question_mark_separator, qs_part = path_part.partition("?") # method if not METHRE.match(method): raise BadStatusLine(method) # version try: if version.startswith("HTTP/"): n1, n2 = version[5:].split(".", 1) version_o = HttpVersion(int(n1), int(n2)) else: raise BadStatusLine(version) except Exception: raise BadStatusLine(version) # read headers ( headers, raw_headers, close, compression, upgrade, chunked, ) = self.parse_headers(lines) if close is None: # then the headers weren't set in the request if version_o <= HttpVersion10: # HTTP 1.0 must asks to not close close = True else: # HTTP 1.1 must ask to close. close = False return RawRequestMessage( method, path, version_o, headers, raw_headers, close, compression, upgrade, chunked, # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based # NOTE: parser does, otherwise it results into the same # NOTE: HTTP Request-Line input producing different # NOTE: `yarl.URL()` objects URL.build( path=path_part, query_string=qs_part, fragment=url_fragment, encoded=True, ), )
def name(self) -> str: scheme = 'https' if self._ssl_context else 'http' return str(URL.build(scheme=scheme, host=self._host, port=self._port))
async def main(): logger.debug('start') address = os.getenv('ADDRESS') base_url = URL.build(scheme='http', host=address, port=8000) logger.debug('base url: %s', base_url) await game(client=Client(base_url))
async def connect(self, host, port, url='/', protocol='ws', cookies=None): url = URL.build(scheme=protocol, host=host, port=port, path=url) await self.connect_url(url, cookies=cookies)
def test_build_without_arguments(): u = URL.build() assert str(u) == ''