
""" Tools for URL handling. """

from __future__ import annotations

import dataclasses
import urllib.parse


@dataclasses.dataclass
class Url:
  """ Helper to represent the components of a URL, including first class support for username, password, host and port. """

  scheme: str = ''
  hostname: str = ''
  path: str = ''
  params: str = ''
  query: str = ''
  fragment: str = ''

  username: str | None = None
  password: str | None = None
  port: int | None = None

  def __str__(self) -> str:
    return urllib.parse.urlunparse((self.scheme, self.netloc, self.path, self.params, self.query, self.fragment))

  @property
  def netloc(self) -> str:
    """ Returns the entire network location with auth and port. """

    auth = self.auth
    if auth:
      return f'{self.auth}@{self.netloc_no_auth}'

    return self.netloc_no_auth

  @property
  def auth(self) -> str | None:
    """ Returns just the auth part of the network location."""

    if self.username or self.password:
      return f'{urllib.parse.quote(self.username or "")}:{urllib.parse.quote(self.password or "")}'

    return None

  @property
  def netloc_no_auth(self) -> str:
    """ Returns the network location without the auth part."""

    if self.port is None:
      return self.hostname

    return f'{self.hostname}:{self.port}'

  @staticmethod
  def of(url: str) -> Url:
    """ Parses the *url* string into its parts.

    Raises:
      ValueError: If an invalid URL is passed (for example if the port number cannot be parsed to an integer).
    """
    parsed = urllib.parse.urlparse(url)
    return Url(
      scheme=parsed.scheme,
      hostname=parsed.hostname or '',
      path=parsed.path,
      params=parsed.params,
      query=parsed.query,
      fragment=parsed.fragment,
      username=parsed.username,
      password=parsed.password,
      port=parsed.port,
    )


""" Tools for URL handling. """

from __future__ import annotations

import dataclasses
import urllib.parse


@dataclasses.dataclass
class Url:
  """ Helper to represent the components of a URL, including first class support for username, password, host and port. """

  scheme: str = ''
  hostname: str = ''
  path: str = ''
  params: str = ''
  query: str = ''
  fragment: str = ''

  username: str | None = None
  password: str | None = None
  port: int | None = None

  def __str__(self) -> str:
    return urllib.parse.urlunparse((self.scheme, self.netloc, self.path, self.params, self.query, self.fragment))

  @property
  def netloc(self) -> str:
    """ Returns the entire network location with auth and port. """

    auth = self.auth
    if auth:
      return f'{self.auth}@{self.netloc_no_auth}'

    return self.netloc_no_auth

  @property
  def auth(self) -> str | None:
    """ Returns just the auth part of the network location."""

    if self.username or self.password:
      return f'{urllib.parse.quote(self.username or "")}:{urllib.parse.quote(self.password or "")}'

    return None

  @property
  def netloc_no_auth(self) -> str:
    """ Returns the network location without the auth part."""

    if self.port is None:
      return self.hostname

    return f'{self.hostname}:{self.port}'

  @staticmethod
  def of(url: str) -> Url:
    """ Parses the *url* string into its parts.

    Raises:
      ValueError: If an invalid URL is passed (for example if the port number cannot be parsed to an integer).
    """
    parsed = urllib.parse.urlparse(url)
    return Url(
      scheme=parsed.scheme,
      hostname=parsed.hostname or '',
      path=parsed.path,
      params=parsed.params,
      query=parsed.query,
      fragment=parsed.fragment,
      username=parsed.username,
      password=parsed.password,
      port=parsed.port,
    )

Tools for URL handling. Helper to represent the components of a URL, including first class support for username, password, host and port. Returns the entire network location with auth and port. Returns just the auth part of the network location. Returns the network location without the auth part. Parses the *url* string into its parts.      Raises:       ValueError: If an invalid URL is passed (for example if the port number cannot be parsed to an integer).