text: from __future__ import unicode_literals
  import re
  from pkg_resources import Requirement as Req
  
  from .fragment import get_hash_info, parse_fragment, parse_extras_require
  from .vcs import VCS, VCS_SCHEMES
  
  
  URI_REGEX = re.compile(
      r'^(?P<scheme>https?|file|ftps?)://(?P<path>[^#]+)'
      r'(#(?P<fragment>\S+))?'
  )
  
  VCS_REGEX = re.compile(
      r'^(?P<scheme>{0})://'.format(r'|'.join(
          [scheme.replace('+', r'\+') for scheme in VCS_SCHEMES])) +
      r'((?P<login>[^/@]+)@)?'
      r'(?P<path>[^#@]+)'
      r'(@(?P<revision>[^#]+))?'
      r'(#(?P<fragment>\S+))?'
  )
  
  VCS_EXT_REGEX = re.compile(
      r'^(?P<scheme>{0})(@)'.format(r'|'.join(
          [scheme.replace('+', r'\+') for scheme in ['git+git']])) +
      r'((?P<login>[^/@]+)@)?'
      r'(?P<path>[^#@]+)'
      r'(@(?P<revision>[^#]+))?'
      r'(#(?P<fragment>\S+))?'
  )
  
  # This matches just about everyting
  LOCAL_REGEX = re.compile(
      r'^((?P<scheme>file)://)?'
      r'(?P<path>[^#]+)' +
      r'(#(?P<fragment>\S+))?'
  )
  
  
  class Requirement(object):
      """
      Represents a single requirementfrom clearml_agent.external.requirements_parser.requirement import Requirement
  
      Typically instances of this class are created with ``Requirement.parse``.
      For local file requirements, there's no verification that the file
      exists. This class attempts to be *dict-like*.
  
      See: http://www.pip-installer.org/en/latest/logic.html
  
      **Members**:
  
      * ``line`` - the actual requirement line being parsed
      * ``editable`` - a boolean whether this requirement is "editable"
      * ``local_file`` - a boolean whether this requirement is a local file/path
      * ``specifier`` - a boolean whether this requirement used a requirement
        specifier (eg. "django>=1.5" or "requirements")
      * ``vcs`` - a string specifying the version control system
      * ``revision`` - a version control system specifier
      * ``name`` - the name of the requirement
      * ``uri`` - the URI if this requirement was specified by URI
      * ``subdirectory`` - the subdirectory fragment of the URI
      * ``path`` - the local path to the requirement
      * ``hash_name`` - the type of hashing algorithm indicated in the line
      * ``hash`` - the hash value indicated by the requirement line
      * ``extras`` - a list of extras for this requirement
        (eg. "mymodule[extra1, extra2]")
      * ``specs`` - a list of specs for this requirement
        (eg. "mymodule>1.5,<1.6" => [('>', '1.5'), ('<', '1.6')])
      """
  
      def __init__(self, line):
          # Do not call this private method
          self.line = line
          self.editable = False
          self.local_file = False
          self.specifier = False
          self.vcs = None
          self.name = None
          self.subdirectory = None
          self.uri = None
          self.path = None
          self.revision = None
          self.hash_name = None
          self.hash = None
          self.extras = []
          self.specs = []
  
      def __repr__(self):
          return '<Requirement: "{0}">'.format(self.line)
  
      def __getitem__(self, key):
          return getattr(self, key)
  
      def keys(self):
          return self.__dict__.keys()
  
      @classmethod
      def parse_editable(cls, line):
          """
          Parses a Requirement from an "editable" requirement which is either
          a local project path or a VCS project URI.
  
          See: pip/req.py:from_editable()
  
          :param line: an "editable" requirement
          :returns: a Requirement instance for the given line
          :raises: ValueError on an invalid requirement
          """
  
          req = cls('-e {0}'.format(line))
          req.editable = True
          vcs_match = VCS_REGEX.match(line) or VCS_EXT_REGEX.match(line)
          local_match = LOCAL_REGEX.match(line)
  
          if vcs_match is not None:
              groups = vcs_match.groupdict()
              if groups.get('login'):
                  req.uri = '{scheme}://{login}@{path}'.format(**groups)
              else:
                  req.uri = '{scheme}://{path}'.format(**groups)
              req.revision = groups['revision']
              if groups['fragment']:
                  fragment = parse_fragment(groups['fragment'])
                  egg = fragment.get('egg')
                  req.name, req.extras = parse_extras_require(egg)
                  req.hash_name, req.hash = get_hash_info(fragment)
                  req.subdirectory = fragment.get('subdirectory')
              for vcs in VCS:
                  if req.uri.startswith(vcs):
                      req.vcs = vcs
          else:
              assert local_match is not None, 'This should match everything'
              groups = local_match.groupdict()
              req.local_file = True
              if groups['fragment']:
                  fragment = parse_fragment(groups['fragment'])
                  egg = fragment.get('egg')
                  req.name, req.extras = parse_extras_require(egg)
                  req.hash_name, req.hash = get_hash_info(fragment)
                  req.subdirectory = fragment.get('subdirectory')
              req.path = groups['path']
  
          return req
  
      @classmethod
      def parse_line(cls, line):
          """
          Parses a Requirement from a non-editable requirement.
  
          See: pip/req.py:from_line()
  
          :param line: a "non-editable" requirement
          :returns: a Requirement instance for the given line
          :raises: ValueError on an invalid requirement
          """
  
          req = cls(line)
  
          vcs_match = VCS_REGEX.match(line) or VCS_EXT_REGEX.match(line)
          uri_match = URI_REGEX.match(line)
          local_match = LOCAL_REGEX.match(line)
  
          if vcs_match is not None:
              groups = vcs_match.groupdict()
              if groups.get('login'):
                  req.uri = '{scheme}://{login}@{path}'.format(**groups)
              else:
                  req.uri = '{scheme}://{path}'.format(**groups)
              req.revision = groups['revision']
              if groups['fragment']:
                  fragment = parse_fragment(groups['fragment'])
                  egg = fragment.get('egg')
                  req.name, req.extras = parse_extras_require(egg)
                  req.hash_name, req.hash = get_hash_info(fragment)
                  req.subdirectory = fragment.get('subdirectory')
              for vcs in VCS:
                  if req.uri.startswith(vcs):
                      req.vcs = vcs
          elif uri_match is not None:
              groups = uri_match.groupdict()
              req.uri = '{scheme}://{path}'.format(**groups)
              if groups['fragment']:
                  fragment = parse_fragment(groups['fragment'])
                  egg = fragment.get('egg')
                  req.name, req.extras = parse_extras_require(egg)
                  req.hash_name, req.hash = get_hash_info(fragment)
                  req.subdirectory = fragment.get('subdirectory')
              if groups['scheme'] == 'file':
                  req.local_file = True
          elif '#egg=' in line:
              # Assume a local file match
              assert local_match is not None, 'This should match everything'
              groups = local_match.groupdict()
              req.local_file = True
              if groups['fragment']:
                  fragment = parse_fragment(groups['fragment'])
                  egg = fragment.get('egg')
                  name, extras = parse_extras_require(egg)
                  req.name = fragment.get('egg')
                  req.hash_name, req.hash = get_hash_info(fragment)
                  req.subdirectory = fragment.get('subdirectory')
              req.path = groups['path']
          else:
              # This is a requirement specifier.
              # Delegate to pkg_resources and hope for the best
              req.specifier = True
              pkg_req = Req.parse(line)
              req.name = pkg_req.unsafe_name
              req.extras = list(pkg_req.extras)
              req.specs = pkg_req.specs
          return req
  
      @classmethod
      def parse(cls, line):
          """
          Parses a Requirement from a line of a requirement file.
  
          :param line: a line of a requirement file
          :returns: a Requirement instance for the given line
          :raises: ValueError on an invalid requirement
          """
          line = line.lstrip()
          if line.startswith('-e') or line.startswith('--editable'):
              # Editable installs are either a local project path
              # or a VCS project URI
              return cls.parse_editable(
                  re.sub(r'^(-e|--editable=?)\s*', '', line))
          elif '@' in line and ('#' not in line or line.index('#') > line.index('@')):
              # Allegro bug fix: support 'name @ git+' entries
              name, uri = line.split('@', 1)
              name = name.strip()
              uri = uri.strip()
              # noinspection PyBroadException
              try:
                  # check if the name is valid & parsed
                  Req.parse(name)
                  # if we are here, name is a valid package name, check if the vcs part is valid
                  if VCS_REGEX.match(uri) or VCS_EXT_REGEX.match(uri):
                      req = cls.parse_line(uri)
                      req.name = name
                      return req
                  elif URI_REGEX.match(uri):
                      req = cls.parse_line(uri)
                      req.name = name
                      req.line = line
                      return req
              except Exception:
                  pass
  
          return cls.parse_line(line)
  