def directory(self, value): """Normalize the value of :attr:`directory` when it's set.""" # Normalize the value of `directory'. set_property(self, "directory", parse_path(value)) # Clear the computed values of `context' and `entries'. clear_property(self, "context") clear_property(self, "entries")
def is_available(self): """ :data:`True` if :attr:`release_gpg_contents` contains the expected header, :data:`False` otherwise. The value of this property is computed by checking whether :attr:`release_gpg_contents` contains the expected ``BEGIN PGP SIGNATURE`` header. This may seem like a rather obscure way of validating a mirror, but it was specifically chosen to detect all sorts of ways in which mirrors can be broken: - Webservers with a broken configuration that return an error page for all URLs. - Mirrors whose domain name registration has expired, where the domain is now being squatted and returns HTTP 200 OK responses for all URLs (whether they "exist" or not). """ value = False if self.release_gpg_contents: value = b'BEGIN PGP SIGNATURE' in self.release_gpg_contents if not value: logger.debug( "Missing GPG header, considering mirror unavailable (%s).", self.release_gpg_url) set_property(self, 'is_available', value) return value
def python_callback(self, value): """Automatically coerce :attr:`python_callback` to a callable value.""" if value: # Python callers get to pass a callable directly. if not callable(value): expression = value # Otherwise we expect a string to parse (from a command line # argument, environment variable or configuration file). callback_path, _, callback_name = expression.partition(':') if os.path.isfile(callback_path): # Callback specified as Python script. script_name = os.path.basename(callback_path) if script_name.endswith('.py'): script_name, _ = os.path.splitext(script_name) environment = dict(__file__=callback_path, __name__=script_name) logger.debug("Loading Python callback from pathname: %s", callback_path) with open(callback_path) as handle: exec(handle.read(), environment) value = environment.get(callback_name) else: # Callback specified as `dotted path'. logger.debug("Loading Python callback from dotted path: %s", callback_path) module = importlib.import_module(callback_path) value = getattr(module, callback_name, None) if not callable(value): raise ValueError(compact(""" The Python callback expression {expr} didn't result in a valid callable! (result: {value}) """, expr=expression, value=value)) else: value = None set_property(self, 'python_callback', value)
def repository(self, value): """Automatically coerce :attr:`repository` values.""" directory = os.path.abspath(value) if not os.path.isdir(directory): msg = "Repository directory doesn't exist! (%s)" raise ValueError(msg % directory) set_property(self, 'repository', PackageRepository(directory))
def test_get_password(self): """Test getting a password from an entry.""" random_password = random_string() entry = PasswordEntry(name="some/random/password", store=object()) set_property( entry, "text", "\n".join([random_password, "", "This is the description"])) self.assertEquals(random_password, entry.password)
def test_get_password(self): """Test getting a password from an entry.""" random_password = random_string() entry = PasswordEntry(name='some/random/password', store=object()) set_property( entry, 'text', '\n'.join([random_password, '', 'This is the description'])) self.assertEquals(random_password, entry.password)
def timestamp_pattern(self, value): """Coerce the value of :attr:`timestamp_pattern` to a compiled regular expression.""" pattern = coerce_pattern(value, re.VERBOSE) for component, required in SUPPORTED_DATE_COMPONENTS: if component not in pattern.groupindex and required: raise ValueError( "Pattern is missing required capture group! (%s)" % component) set_property(self, 'timestamp_pattern', pattern)
def port_number(self): """A dynamically selected free ephemeral port number (an integer between 49152 and 65535).""" timer = Timer() logger.debug("Looking for free ephemeral port number ..") for i in itertools.count(1): value = self.ephemeral_port_number set_property(self, 'port_number', value) if not self.is_connected: logger.debug("Found free ephemeral port number %s after %s (took %s).", self, pluralize(i, "attempt"), timer) return value
def port_number(self): """A dynamically selected free ephemeral port number (an integer between 49152 and 65535).""" timer = Timer() logger.debug("Looking for free ephemeral port number ..") for i in itertools.count(1): value = self.ephemeral_port_number set_property(self, 'port_number', value) if not self.is_connected: logger.debug("Found free ephemeral port number %s after %s (took %s).", value, pluralize(i, "attempt"), timer) return value
def architecture(self): """ The name of the Debian package architecture (a string like 'i386' or 'amd64'). The package architecture is used to detect whether `Debian LTS`_ status applies to the given system (the Debian LTS team supports a specific subset of package architectures). .. _Debian LTS: https://wiki.debian.org/LTS """ value = self.context.capture('dpkg', '--print-architecture') set_property(self, 'architecture', value) return value
def test_format_text(self): """Test human friendly formatting of password store entries.""" entry = PasswordEntry(name='some/random/password', store=object()) set_property(entry, 'text', random_string()) self.assertEquals( # We enable ANSI escape sequences but strip them before we # compare the generated string. This may seem rather pointless # but it ensures that the relevant code paths are covered :-). dedent( ansi_strip( entry.format_text(include_password=True, use_colors=True))), dedent(''' some / random / password Password: {value} ''', value=entry.text))
def ssh_alias(self, value): """Set the value of :attr:`ssh_alias` and optionally :attr:`ssh_user`.""" user, _, host = value.partition('@') if user and host: set_property(self, 'ssh_alias', host) set_property(self, 'ssh_user', user) else: set_property(self, 'ssh_alias', value)
def is_available(self): """ :data:`True` if :attr:`release_gpg_contents` contains the expected header, :data:`False` otherwise. The value of this property is computed by checking whether :attr:`release_gpg_contents` contains the expected ``BEGIN PGP SIGNATURE`` header. This may seem like a rather obscure way of validating a mirror, but it was specifically chosen to detect all sorts of ways in which mirrors can be broken: - Webservers with a broken configuration that return an error page for all URLs. - Mirrors whose domain name registration has expired, where the domain is now being squatted and returns HTTP 200 OK responses for all URLs (whether they "exist" or not). """ value = False if self.release_gpg_contents: value = b'BEGIN PGP SIGNATURE' in self.release_gpg_contents if not value: logger.debug("Missing GPG header, considering mirror unavailable (%s).", self.release_gpg_url) set_property(self, 'is_available', value) return value
def objects(self): """ The values associated to the tag (a :class:`set`). If :attr:`objects` isn't set it defaults to a computed value: - If :attr:`identifier` is :data:`DEFAULT_TAG_NAME` then :func:`~Scope.get_all_objects()` is used to get the associated values. - If :attr:`expression` is set it will be evaluated and the matching objects will be returned. - Otherwise a new, empty :class:`set` is created, bound to the :class:`Tag` and returned. """ if self.identifier == DEFAULT_TAG_NAME: return self.scope.get_all_objects() elif self.expression: return self.scope.evaluate_raw(self.expression) else: value = set() set_property(self, 'objects', value) return value
def mirror_url(self, value): """Normalize the mirror URL when set.""" set_property(self, 'mirror_url', normalize_mirror_url(value))
def lintian_enabled(self, value): """Automatically coerce :attr:`lintian_enabled` to a boolean value.""" set_property(self, 'lintian_enabled', coerce_boolean(value))
def destination(self, value): """Automatically coerce strings to :class:`.Destination` objects.""" if not isinstance(value, Destination): value = Destination(expression=value) set_property(self, 'destination', value) clear_property(self, 'destination_context')
def expression(self, value): """Set `expression` and clear `objects`.""" set_property(self, 'expression', value) clear_property(self, 'id_or_expr') clear_property(self, 'objects')
def source(self, value): """Automatically coerce :attr:`source` to a :class:`Location`.""" set_property(self, "source", Location(expression=value))
def name(self, value): """Set `name` and `identifier`.""" set_property(self, 'name', value) clear_property(self, 'id_or_expr') clear_property(self, 'identifier')
def port_number(self, value): """Automatically coerce port numbers to integers.""" set_property(self, 'port_number', int(value))
def objects(self, value): """Set `objects` and clear `expression`.""" set_property(self, 'objects', set(value)) clear_property(self, 'id_or_expr') clear_property(self, 'expression')
def target(self, value): """Automatically coerce :attr:`target` to a :class:`Location`.""" set_property(self, "target", Location(expression=value))