def safe_join(base, *paths): """ Joins one or more path components to the base path component intelligently. Returns a normalized, absolute version of the final path. The final path must be located inside of the base path component (otherwise a ValueError is raised). """ base = force_text(base) paths = [force_text(p) for p in paths] final_path = abspathu(join(base, *paths)) base_path = abspathu(base) # Ensure final_path starts with base_path (using normcase to ensure we # don't false-negative on case insensitive operating systems like Windows), # further, one of the following conditions must be true: # a) The next character is the path separator (to prevent conditions like # safe_join("/dir", "/../d")) # b) The final path must be the same as the base path. # c) The base path must be the most root path (meaning either "/" or "C:\\") if (not normcase(final_path).startswith(normcase(base_path + sep)) and normcase(final_path) != normcase(base_path) and dirname(normcase(base_path)) != normcase(base_path)): raise SuspiciousFileOperation( 'The joined path ({}) is located outside of the base path ' 'component ({})'.format(final_path, base_path)) return final_path
def popen_wrapper(args, os_err_exc_type=CommandError, stdout_encoding='utf-8'): """ Friendly wrapper around Popen. Returns stdout output, stderr output and OS status code. """ try: p = Popen(args, shell=False, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt') except OSError as e: strerror = force_text(e.strerror, DEFAULT_LOCALE_ENCODING, strings_only=True) six.reraise( os_err_exc_type, os_err_exc_type('Error executing %s: %s' % (args[0], strerror)), sys.exc_info()[2]) output, errors = p.communicate() return (force_text(output, stdout_encoding, strings_only=True, errors='strict'), force_text(errors, DEFAULT_LOCALE_ENCODING, strings_only=True, errors='replace'), p.returncode)
def get_dump_object(self, obj): data = OrderedDict([('model', force_text(obj._meta))]) if not self.use_natural_primary_keys or not hasattr( obj, 'natural_key'): data["pk"] = force_text(obj._get_pk_val(), strings_only=True) data['fields'] = self._current return data
def format(self, formatstr): pieces = [] for i, piece in enumerate(re_formatchars.split(force_text(formatstr))): if i % 2: if type(self.data) is datetime.date and hasattr( TimeFormat, piece): raise TypeError( "The format for date objects may not contain " "time-related format specifiers (found '%s')." % piece) pieces.append(force_text(getattr(self, piece)())) elif piece: pieces.append(re_escaped.sub(r'\1', piece)) return ''.join(pieces)
def wrap(text, width): """ A word-wrap function that preserves existing line breaks. Expects that existing line breaks are posix newlines. All white space is preserved except added line breaks consume the space on which they break the line. Long words are not wrapped, so the output text may have lines longer than ``width``. """ text = force_text(text) def _generator(): for line in text.splitlines(True): # True keeps trailing linebreaks max_width = min((line.endswith('\n') and width + 1 or width), width) while len(line) > max_width: space = line[:max_width + 1].rfind(' ') + 1 if space == 0: space = line.find(' ') + 1 if space == 0: yield line line = '' break yield '%s\n' % line[:space - 1] line = line[space:] max_width = min((line.endswith('\n') and width + 1 or width), width) if line: yield line return ''.join(_generator())
def __init__(self, app_label, name, fields, options=None, bases=None, managers=None): self.app_label = app_label self.name = force_text(name) self.fields = fields self.options = options or {} self.bases = bases or (models.Model, ) self.managers = managers or [] # Sanity-check that fields is NOT a dict. It must be ordered. if isinstance(self.fields, dict): raise ValueError( "ModelState.fields cannot be a dict - it must be a list of 2-tuples." ) for name, field in fields: # Sanity-check that fields are NOT already bound to a model. if hasattr(field, 'model'): raise ValueError( 'ModelState.fields cannot be bound to a model - "%s" is.' % name) # Sanity-check that relation fields are NOT referring to a model class. if field.is_relation and hasattr(field.related_model, '_meta'): raise ValueError( 'ModelState.fields cannot refer to a model class - "%s.to" does. ' 'Use a string reference instead.' % name) if field.many_to_many and hasattr(field.remote_field.through, '_meta'): raise ValueError( 'ModelState.fields cannot refer to a model class - "%s.through" does. ' 'Use a string reference instead.' % name)
def get_prep_value(self, value): value = super(HStoreField, self).get_prep_value(value) if isinstance(value, dict): prep_value = {} for key, val in value.items(): key = force_text(key) if val is not None: val = force_text(val) prep_value[key] = val value = prep_value if isinstance(value, list): value = [force_text(item) for item in value] return value
def __call__(self, value): """ Validates that the input matches the regular expression if inverse_match is False, otherwise raises ValidationError. """ if not (self.inverse_match is not bool( self.regex.search(force_text(value)))): raise ValidationError(self.message, code=self.code)
def pgettext(context, message): msg_with_ctxt = "%s%s%s" % (context, CONTEXT_SEPARATOR, message) result = ugettext(msg_with_ctxt) if CONTEXT_SEPARATOR in result: # Translation not found # force unicode, because lazy version expects unicode result = force_text(message) return result
def m2m_convert(value): if hasattr(value, '__iter__') and not isinstance( value, six.text_type): return model._default_manager.db_manager( db).get_by_natural_key(*value).pk else: return force_text(model._meta.pk.to_python(value), strings_only=True)
def verbose_name_raw(self): """ There are a few places where the untranslated verbose name is needed (so that we get the same value regardless of currently active locale). """ with override(None): return force_text(self.verbose_name)
def to_unicode(s): """ Convert strings to Unicode objects (and return all other data types unchanged). """ if isinstance(s, six.string_types): return force_text(s) return s
def urlquote_plus(url, safe=''): """ A version of Python's urllib.quote_plus() function that can operate on unicode strings. The url is first UTF-8 encoded before quoting. The returned string can safely be used as part of an argument to a subsequent iri_to_uri() call without double-quoting occurring. """ return force_text(quote_plus(force_str(url), force_str(safe)))
def get_directory_name(self): warnings.warn( 'FileField now delegates file name and folder processing to the ' 'storage. get_directory_name() will be removed in Django 2.0.', RemovedInDjango20Warning, stacklevel=2) return os.path.normpath( force_text(datetime.datetime.now().strftime( force_str(self.upload_to))))
def get_table_description(self, cursor, table_name): "Returns a description of the table, with the DB-API cursor.description interface." # As cursor.description does not return reliably the nullable property, # we have to query the information_schema (#7783) cursor.execute( """ SELECT column_name, is_nullable, column_default FROM information_schema.columns WHERE table_name = %s""", [table_name]) field_map = {line[0]: line[1:] for line in cursor.fetchall()} cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) return [ FieldInfo(*((force_text(line[0]), ) + line[1:6] + (field_map[force_text(line[0])][0] == 'YES', field_map[force_text(line[0])][1]))) for line in cursor.description ]
def __iter__(self): if hasattr(self, 'error_dict'): for field, errors in self.error_dict.items(): yield field, list(ValidationError(errors)) else: for error in self.error_list: message = error.message if error.params: message %= error.params yield force_text(message)
def get_valid_filename(s): """ Returns the given string converted to a string that can be used for a clean filename. Specifically, leading and trailing spaces are removed; other spaces are converted to underscores; and anything that is not a unicode alphanumeric, dash, underscore, or dot, is removed. >>> get_valid_filename("john's portrait in 2004.jpg") 'johns_portrait_in_2004.jpg' """ s = force_text(s).strip().replace(' ', '_') return re.sub(r'(?u)[^-\w.]', '', s)
def call_command(command_name, *args, **options): """ Calls the given command, with the given options and args/kwargs. This is the primary API you should use for calling specific commands. `name` may be a string or a command object. Using a string is preferred unless the command object is required for further processing or testing. Some examples: call_command('migrate') call_command('shell', plain=True) call_command('sqlmigrate', 'myapp') from arouse._dj.core.management.commands import flush cmd = flush.Command() call_command(cmd, verbosity=0, interactive=False) # Do something with cmd ... """ if isinstance(command_name, BaseCommand): # Command object passed in. command = command_name command_name = command.__class__.__module__.split('.')[-1] else: # Load the command object by name. try: app_name = get_commands()[command_name] except KeyError: raise CommandError("Unknown command: %r" % command_name) if isinstance(app_name, BaseCommand): # If the command is already loaded, use it directly. command = app_name else: command = load_command_class(app_name, command_name) # Simulate argument parsing to get the option defaults (see #10080 for details). parser = command.create_parser('', command_name) # Use the `dest` option name from the parser option opt_mapping = { sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest for s_opt in parser._actions if s_opt.option_strings } arg_options = {opt_mapping.get(key, key): value for key, value in options.items()} defaults = parser.parse_args(args=[force_text(a) for a in args]) defaults = dict(defaults._get_kwargs(), **arg_options) # Move positional args out of options to mimic legacy optparse args = defaults.pop('args', ()) if 'skip_checks' not in options: defaults['skip_checks'] = True return command.execute(*args, **defaults)
def get_text_list(list_, last_word=ugettext_lazy('or')): """ >>> get_text_list(['a', 'b', 'c', 'd']) 'a, b, c or d' >>> get_text_list(['a', 'b', 'c'], 'and') 'a, b and c' >>> get_text_list(['a', 'b'], 'and') 'a and b' >>> get_text_list(['a']) 'a' >>> get_text_list([]) '' """ if len(list_) == 0: return '' if len(list_) == 1: return force_text(list_[0]) return '%s %s %s' % ( # Translators: This string is used as a separator between list elements _(', ').join(force_text(i) for i in list_[:-1]), force_text(last_word), force_text(list_[-1]))
def add_truncation_text(self, text, truncate=None): if truncate is None: truncate = pgettext('String to return when truncating text', '%(truncated_text)s...') truncate = force_text(truncate) if '%(truncated_text)s' in truncate: return truncate % {'truncated_text': text} # The truncation text didn't contain the %(truncated_text)s string # replacement argument so just append it to the text. if text.endswith(truncate): # But don't append the truncation text if the current text already # ends in this. return text return '%s%s' % (text, truncate)
def generate_filename(self, instance, filename): """ Apply (if callable) or prepend (if a string) upload_to to the filename, then delegate further processing of the name to the storage backend. Until the storage layer, all file paths are expected to be Unix style (with forward slashes). """ if callable(self.upload_to): filename = self.upload_to(instance, filename) else: dirname = force_text(datetime.datetime.now().strftime( force_str(self.upload_to))) filename = posixpath.join(dirname, filename) return self.storage.generate_filename(filename)
def get_table_description(self, cursor, table_name): "Returns a description of the table, with the DB-API cursor.description interface." self.cache_bust_counter += 1 cursor.execute("SELECT * FROM {} WHERE ROWNUM < 2 AND {} > 0".format( self.connection.ops.quote_name(table_name), self.cache_bust_counter)) description = [] for desc in cursor.description: name = force_text( desc[0] ) # cx_Oracle always returns a 'str' on both Python 2 and 3 name = name % { } # cx_Oracle, for some reason, doubles percent signs. description.append(FieldInfo(*(name.lower(), ) + desc[1:])) return description
def construct_managers(self): "Deep-clone the managers using deconstruction" # Sort all managers by their creation counter sorted_managers = sorted(self.managers, key=lambda v: v[1].creation_counter) for mgr_name, manager in sorted_managers: mgr_name = force_text(mgr_name) as_manager, manager_path, qs_path, args, kwargs = manager.deconstruct( ) if as_manager: qs_class = import_string(qs_path) yield mgr_name, qs_class.as_manager() else: manager_class = import_string(manager_path) yield mgr_name, manager_class(*args, **kwargs)
def slugify(value, allow_unicode=False): """ Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens. Remove characters that aren't alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip leading and trailing whitespace. """ value = force_text(value) if allow_unicode: value = unicodedata.normalize('NFKC', value) value = re.sub('[^\w\s-]', '', value, flags=re.U).strip().lower() return mark_safe(re.sub('[-\s]+', '-', value, flags=re.U)) value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') value = re.sub('[^\w\s-]', '', value).strip().lower() return mark_safe(re.sub('[-\s]+', '-', value))
def smart_split(text): r""" Generator that splits a string by spaces, leaving quoted phrases together. Supports both single and double quotes, and supports escaping quotes with backslashes. In the output, strings will keep their initial and trailing quote marks and escaped quotes will remain escaped (the results can then be further processed with unescape_string_literal()). >>> list(smart_split(r'This is "a person\'s" test.')) ['This', 'is', '"a person\\\'s"', 'test.'] >>> list(smart_split(r"Another 'person\'s' test.")) ['Another', "'person\\'s'", 'test.'] >>> list(smart_split(r'A "\"funky\" style" test.')) ['A', '"\\"funky\\" style"', 'test.'] """ text = force_text(text) for bit in smart_split_re.finditer(text): yield bit.group(0)
def __call__(self, value): value = force_text(value) # Check first if the scheme is valid scheme = value.split('://')[0].lower() if scheme not in self.schemes: raise ValidationError(self.message, code=self.code) # Then check full URL try: super(URLValidator, self).__call__(value) except ValidationError as e: # Trivial case failed. Try for possible IDN domain if value: try: scheme, netloc, path, query, fragment = urlsplit(value) except ValueError: # for example, "Invalid IPv6 URL" raise ValidationError(self.message, code=self.code) try: netloc = netloc.encode('idna').decode( 'ascii') # IDN -> ACE except UnicodeError: # invalid domain part raise e url = urlunsplit((scheme, netloc, path, query, fragment)) super(URLValidator, self).__call__(url) else: raise else: # Now verify IPv6 in the netloc part host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$', urlsplit(value).netloc) if host_match: potential_ip = host_match.groups()[0] try: validate_ipv6_address(potential_ip) except ValidationError: raise ValidationError(self.message, code=self.code) url = value # The maximum length of a full host name is 253 characters per RFC 1034 # section 3.1. It's defined to be 255 bytes or less, but this includes # one byte for the length of the name and one byte for the trailing dot # that's used to indicate absolute names in DNS. if len(urlsplit(value).netloc) > 253: raise ValidationError(self.message, code=self.code)
def is_safe_url(url, host=None): """ Return ``True`` if the url is a safe redirection (i.e. it doesn't point to a different host and uses a safe scheme). Always returns ``False`` on an empty url. """ if url is not None: url = url.strip() if not url: return False if six.PY2: try: url = force_text(url) except UnicodeDecodeError: return False # Chrome treats \ completely as / in paths but it could be part of some # basic auth credentials so we need to check both URLs. return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host)
def __call__(self, value): value = force_text(value) if not value or '@' not in value: raise ValidationError(self.message, code=self.code) user_part, domain_part = value.rsplit('@', 1) if not self.user_regex.match(user_part): raise ValidationError(self.message, code=self.code) if (domain_part not in self.domain_whitelist and not self.validate_domain_part(domain_part)): # Try for possible IDN domain-part try: domain_part = domain_part.encode('idna').decode('ascii') if self.validate_domain_part(domain_part): return except UnicodeError: pass raise ValidationError(self.message, code=self.code)
def get_table_description(self, cursor, table_name): """ Returns a description of the table, with the DB-API cursor.description interface." """ # information_schema database gives more accurate results for some figures: # - varchar length returned by cursor.description is an internal length, # not visible length (#5725) # - precision and scale (for decimal fields) (#5014) # - auto_increment is not available in cursor.description cursor.execute( """ SELECT column_name, data_type, character_maximum_length, numeric_precision, numeric_scale, extra, column_default FROM information_schema.columns WHERE table_name = %s AND table_schema = DATABASE()""", [table_name]) field_info = {line[0]: InfoLine(*line) for line in cursor.fetchall()} cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name)) def to_int(i): return int(i) if i is not None else i fields = [] for line in cursor.description: col_name = force_text(line[0]) fields.append( FieldInfo(*((col_name, ) + line[1:3] + ( to_int(field_info[col_name].max_len) or line[3], to_int(field_info[col_name].num_prec) or line[4], to_int(field_info[col_name].num_scale) or line[5], line[6], field_info[col_name].extra, field_info[col_name].column_default, )))) return fields
def urlunquote_plus(quoted_url): """ A wrapper for Python's urllib.unquote_plus() function that can operate on the result of django.utils.http.urlquote_plus(). """ return force_text(unquote_plus(force_str(quoted_url)))