示例#1
0
def obj_docstring(obj):
    ''' Return a docstring for `obj` which has been passed through `stripped_dedent`.

      This function uses `obj.__doc__` if it is not `None`,
      otherwise `getcomments(obj)` if that is not `None`,
      otherwise `''`.
      The chosen string is passed through `stripped_dedent` before return.
  '''
    docstring = getattr(obj, '__doc__', None)
    if docstring is None:
        docstring = '\n'.join(
            map(lambda line: cutprefix(line, '# '),
                (getcomments(obj) or '').rstrip().split('\n')))
    return stripped_dedent(docstring)
示例#2
0
  def subcommand_usage_text(
      cls, subcmd, usage_format_mapping=None, short=False
  ):
    ''' Return the usage text for a subcommand.

        Parameters:
        * `subcmd`: the subcommand name
        * `short`: just include the first line of the usage message,
          intented for when there are many subcommands
    '''
    method = cls.subcommands()[subcmd].method
    subusage = None
    # support (method, get_suboptions)
    try:
      classy = issubclass(method, BaseCommand)
    except TypeError:
      classy = False
    if classy:
      # first paragraph of the class usage text
      doc = method.usage_text(cmd=subcmd)
      subusage_format, *_ = cutprefix(doc, 'Usage:').lstrip().split("\n\n", 1)
    else:
      # extract the usage from the object docstring
      doc = obj_docstring(method)
      if doc:
        if 'Usage:' in doc:
          # extract the Usage: paragraph
          pre_usage, post_usage = doc.split('Usage:', 1)
          pre_usage = pre_usage.strip()
          post_usage_format, *_ = post_usage.split('\n\n', 1)
          subusage_format = stripped_dedent(post_usage_format)
        else:
          # extract the first paragraph
          subusage_format, *_ = doc.split('\n\n', 1)
      else:
        # default usage text - include the docstring below a header
        subusage_format = "\n  ".join(
            ['{cmd} ...'] + [doc.split('\n\n', 1)[0]]
        )
    if subusage_format:
      if short:
        subusage_format, *_ = subusage_format.split('\n', 1)
      mapping = dict(sys.modules[method.__module__].__dict__)
      if usage_format_mapping:
        mapping.update(usage_format_mapping)
      mapping.update(cmd=subcmd)
      subusage = subusage_format.format_map(mapping)
    return subusage or None
示例#3
0
 def __getattr__(self, attr):
   field_name = cutprefix(attr, 'by_')
   if field_name is not attr:
     with Pfx("%s.%s", type(self).__name__, attr):
       pk_name = self.IndexedSetMixin__pk
       assert pk_name, "empty .IndexedSetMixin__pk"
       by_pk = 'by_' + pk_name
       indexed = self.__indexed
       with self._lock:
         if field_name in indexed:
           return self.__dict__[attr]
         by_map = {}
         if field_name == pk_name:
           records = self.scan()
         else:
           records = getattr(self, by_pk).values()
         # load the
         ##warned = set()
         i = 0
         for i, record in enumerate(records, 1):
           try:
             field_value = record[field_name]
           except KeyError:
             if field_name == pk_name:
               warning("no primary key %r: %r", field_name, record)
             continue
           ##if field_value in by_map:
           ##  if field_value not in warned:
           ##    warning("multiple records for %r", field_value)
           ##    warned.add(field_value)
           by_map[field_value] = record
         setattr(self, attr, by_map)
         indexed.add(field_name)
         if field_name == pk_name:
           self.__scan_length = i
     return by_map
   if attr == '_IndexedSetMixin__indexed':
     # .__indexed
     indexed = self.__indexed = set()
     return indexed
   try:
     supergetattr = super().__getattr__
   except AttributeError:
     return getattr(type(self), attr)
   else:
     return supergetattr(attr)
示例#4
0
 def __init__(self, parent, *a, key=None, fixed_size=None, **kw):
   for attr in dir(self):
     K = cutprefix(attr, 'WIDGET_')
     if K is not attr:
       v = getattr(self, attr)
       if v is not None:
         kw.setdefault(K.lower(), v)
   if fixed_size is None:
     fixed_size = (None, None)
   self.__parent = parent
   self.fixed_size = fixed_size
   if self.fixed_width is not None:
     kw.update(width=self.fixed_width)
   if self.fixed_height is not None:
     kw.update(height=self.fixed_height)
   super().__init__(parent, *a, **kw)
   if key is None:
     key = uuid4()
   self.key = key
示例#5
0
 def add(self, bookpath):
     ''' Add a book file via the `calibredb add` command.
     Return the database id.
 '''
     cp = self.calibredb('add',
                         '--duplicates',
                         bookpath,
                         subp_options=dict(stdin=DEVNULL,
                                           capture_output=True,
                                           text=True))
     # Extract the database id from the "calibredb add" output.
     dbids = []
     for line in cp.stdout.split('\n'):
         line_sfx = cutprefix(line, 'Added book ids:')
         if line_sfx is not line:
             dbids.extend(map(lambda s: int(s.strip()),
                              line_sfx.split(',')))
     dbid, = dbids  # pylint: disable=unbalanced-tuple-unpacking
     return dbid
示例#6
0
 def from_class(command_cls):
   ''' Return a mapping of subcommand names to subcommand specifications
       for class attributes which commence with
       `command_cls.SUBCOMMAND_METHOD_PREFIX`,
       by default `'cmd_'`.
   '''
   prefix = command_cls.SUBCOMMAND_METHOD_PREFIX
   subcommands_map = {}
   for attr in dir(command_cls):
     if attr.startswith(prefix):
       subcmd = cutprefix(attr, prefix)
       method = getattr(command_cls, attr)
       subcommands_map[subcmd] = (
           _ClassSubCommand(
               subcmd,
               method,
               usage_mapping=dict(getattr(method, 'USAGE_KEYWORDS', ()))
           ) if isclass(method) else _MethodSubCommand(
               subcmd,
               method,
               usage_mapping=dict(getattr(command_cls, 'USAGE_KEYWORDS', ()))
           )
       )
   return subcommands_map
示例#7
0
 def unprefixify_key(key, prefix):
   ''' Return the internal subkey (unprefixed) from the external `key`.
   '''
   assert key.startswith(prefix), \
       "key:%r does not start with prefix:%r" % (key, prefix)
   return cutprefix(key, prefix)
示例#8
0
  def from_spec(cls, spec):
    ''' Construct an instance from a connection spec string
        of the form [`tcp:`|`ssl:`][*user*`@`]*[tcp_host!]server_hostname*[`:`*port*].

        The optional prefixes `tcp:` and `ssl:` indicate that the connection
        should be cleartext or SSL/TLS respectively.
        The default is SSL/TLS.
    '''
    spec2 = cutprefix(spec, 'tcp:')
    if spec2 is not spec:
      spec = spec2
      use_ssl = False
    else:
      spec = cutprefix(spec, 'ssl:')
      use_ssl = True
    # see if what's left after the mode matches a netrc account name
    account_entry = NetrcEntry.by_account(spec)
    if account_entry.machine is None:
      account_entry = None
    else:
      # a match, use the machine name as the spec
      spec = account_entry.machine
    try:
      user, hostpart = spec.split('@', 1)
    except ValueError:
      # no user specified, use a default
      hostpart = spec
      current_user = getpwuid(geteuid()).pw_name
      if account_entry:
        if account_entry.login:
          user = account_entry.login
        else:
          # see if the account name has a user part
          try:
            user, _ = account_entry.account.split('@', 1)
          except ValueError:
            user = current_user
      else:
        user = current_user
    try:
      host, port = hostpart.split(':')
    except ValueError:
      host = hostpart
      port = 995 if use_ssl else 110
    else:
      port = int(port)
    try:
      tcp_host, sni_host = host.split('!', 1)
    except ValueError:
      # get the SNI name from the account name
      if account_entry:
        tcp_host = host
        try:
          _, sni_host = account_entry.account.split('@', 1)
        except ValueError:
          sni_host = account_entry.account
      else:
        tcp_host, sni_host = host, host
    conn_spec = cls(
        user=user, host=tcp_host, sni_host=sni_host, port=port, ssl=use_ssl
    )
    ##print("conn_spec =", conn_spec)
    return conn_spec
示例#9
0
  def __init__(self, db_url, serial_sessions=None):
    ''' Initialise the ORM.

        If `serial_sessions` is true (default `False`)
        then allocate a lock to serialise session allocation.
        This might be chosen with SQL backends which do not support
        concurrent sessions such as SQLite.

        In the case of SQLite there's a small inbuilt timeout in
        an attempt to serialise transactions but it is possible to
        exceed it easily and recovery is usually infeasible.
        Instead we use the `serial_sessions` option to obtain a
        mutex before allocating a session.
    '''
    db_fspath = cutprefix(db_url, 'sqlite:///')
    if db_fspath is db_url:
      # unchanged - no leading "sqlite:///"
      if db_url.startswith(('/', './', '../')) or '://' not in db_url:
        # turn filesystenm pathnames into SQLite db URLs
        db_fspath = abspath(db_url)
        db_url = 'sqlite:///' + db_url
      else:
        # no fs path
        db_fspath = None
    else:
      # starts with sqlite:///, we have the db_fspath
      pass
    self.db_url = db_url
    self.db_fspath = db_fspath
    is_sqlite = db_url.startswith('sqlite://')
    if serial_sessions is None:
      # serial SQLite sessions
      serial_sessions = is_sqlite
    elif not serial_sessions:
      if is_sqlite:
        warning(
            "serial_sessions specified as %r, but is_sqlite=%s:"
            " this may cause trouble with multithreaded use",
            serial_sessions,
            is_sqlite,
        )
    self.serial_sessions = serial_sessions or is_sqlite
    self._lockfilepath = None
    self.Base = declarative_base()
    self.sqla_state = SQLAState(
        orm=self, engine=None, sessionmaker=None, session=None
    )
    if serial_sessions:
      self._serial_sessions_lock = Lock()
    else:
      self._engine = None
      self._sessionmaker_raw = None
    self.db_url = db_url
    self.engine_keywords = {}
    self.engine_keywords = dict(
        case_sensitive=True,
        echo=(
            bool(os.environ.get('DEBUG'))
            or 'echo' in os.environ.get('SQLTAGS_MODES', '').split(',')
        ),  # 'debug'
    )
    if is_sqlite:
      # do not pool these connects and disable the Thread check
      # because we
      self.engine_keywords.update(
          poolclass=NullPool, connect_args={'check_same_thread': False}
      )
    self.declare_schema()
示例#10
0
 def usage_format(self):
   ''' Return the usage format string from the class.
   '''
   doc = self.method.usage_text(cmd=self.cmd)
   subusage_format, *_ = cutprefix(doc, 'Usage:').lstrip().split("\n\n", 1)
   return subusage_format