예제 #1
0
def archive_zip(directory, archive):
    """Archive the directory to the given tar/gz archive filename.

    Args:
      directory: A string, the name of the directory to archive.
      archive: A string, the name of the file to output.
    """
    # Figure out optimal level of compression among the supported ones in this
    # installation.
    for spec, compression in [('lzma', zipfile.ZIP_LZMA),
                              ('bz2', zipfile.ZIP_BZIP2),
                              ('zlib', zipfile.ZIP_DEFLATED)]:
        if importlib.util.find_spec(spec):
            zip_compression = compression
            break
    else:
        # Default is no compression.
        zip_compression = zipfile.ZIP_STORED

    with file_utils.chdir(directory), zipfile.ZipFile(
            archive, 'w', compression=zip_compression) as archfile:
        for root, dirs, files in os.walk(directory):
            for filename in files:
                relpath = path.relpath(path.join(root, filename), directory)
                archfile.write(relpath)
예제 #2
0
def _parse_recursive(sources, log_timings, encoding=None):
    """Parse Beancount input, run its transformations and validate it.

    Recursively parse a list of files or strings and their include files and
    return an aggregate of parsed directives, errors, and the top-level
    options-map. If the same file is being parsed twice, ignore it and issue an
    error.

    Args:
      sources: A list of (filename-or-string, is-filename) where the first
        element is a string, with either a filename or a string to be parsed directly,
        and the second argument is a boolean that is true if the first is a filename.
        You may provide a list of such arguments to be parsed. Filenames must be absolute
        paths.
      log_timings: A function to write timings to, or None, if it should remain quiet.
      encoding: A string or None, the encoding to decode the input filename with.
    Returns:
      A tuple of (entries, parse_errors, options_map).
    """
    assert isinstance(sources, list) and all(
        isinstance(el, tuple) for el in sources)

    # Current parse state.
    entries, parse_errors = [], []
    options_map = None

    # A stack of sources to be parsed.
    source_stack = list(sources)

    # A list of absolute filenames that have been parsed in the past, used to
    # detect and avoid duplicates (cycles).
    filenames_seen = set()

    with misc_utils.log_time('beancount.parser.parser', log_timings, indent=1):
        while source_stack:
            source, is_file = source_stack.pop(0)
            is_top_level = options_map is None

            # If the file is encrypted, read it in and process it as a string.
            if is_file:
                cwd = path.dirname(source)
                source_filename = source
                if encryption.is_encrypted_file(source):
                    source = encryption.read_encrypted_file(source)
                    is_file = False
            else:
                # If we're parsing a string, the CWD is the current process
                # working directory.
                cwd = os.getcwd()
                source_filename = None

            if is_file:
                # All filenames here must be absolute.
                assert path.isabs(source)
                filename = path.normpath(source)

                # Check for file previously parsed... detect duplicates.
                if filename in filenames_seen:
                    parse_errors.append(
                        LoadError(
                            data.new_metadata("<load>", 0),
                            'Duplicate filename parsed: "{}"'.format(filename),
                            None))
                    continue

                # Check for a file that does not exist.
                if not path.exists(filename):
                    parse_errors.append(
                        LoadError(data.new_metadata("<load>", 0),
                                  'File "{}" does not exist'.format(filename),
                                  None))
                    continue

                # Parse a file from disk directly.
                filenames_seen.add(filename)
                with misc_utils.log_time('beancount.parser.parser.parse_file',
                                         log_timings,
                                         indent=2):
                    (src_entries, src_errors,
                     src_options_map) = parser.parse_file(filename,
                                                          encoding=encoding)

                cwd = path.dirname(filename)
            else:
                # Encode the contents if necessary.
                if encoding:
                    if isinstance(source, bytes):
                        source = source.decode(encoding)
                    source = source.encode('ascii', 'replace')

                # Parse a string buffer from memory.
                with misc_utils.log_time(
                        'beancount.parser.parser.parse_string',
                        log_timings,
                        indent=2):
                    (src_entries, src_errors,
                     src_options_map) = parser.parse_string(
                         source, source_filename)

            # Merge the entries resulting from the parsed file.
            entries.extend(src_entries)
            parse_errors.extend(src_errors)

            # We need the options from the very top file only (the very
            # first file being processed). No merging of options should
            # occur.
            if is_top_level:
                options_map = src_options_map
            else:
                aggregate_options_map(options_map, src_options_map)

            # Add includes to the list of sources to process. chdir() for glob,
            # which uses it indirectly.
            include_expanded = []
            with file_utils.chdir(cwd):
                for include_filename in src_options_map['include']:
                    matched_filenames = glob.glob(include_filename,
                                                  recursive=True)
                    if matched_filenames:
                        include_expanded.extend(matched_filenames)
                    else:
                        parse_errors.append(
                            LoadError(
                                data.new_metadata("<load>", 0),
                                'File glob "{}" does not match any files'.
                                format(include_filename), None))
            for include_filename in include_expanded:
                if not path.isabs(include_filename):
                    include_filename = path.join(cwd, include_filename)
                include_filename = path.normpath(include_filename)

                # Add the include filenames to be processed later.
                source_stack.append((include_filename, True))

    # Make sure we have at least a dict of valid options.
    if options_map is None:
        options_map = options.OPTIONS_DEFAULTS.copy()

    # Save the set of parsed filenames in options_map.
    options_map['include'] = sorted(filenames_seen)

    return entries, parse_errors, options_map
예제 #3
0
 def test_chdir_contextmanager(self):
     with file_utils.chdir(tempfile.gettempdir()) as tmpdir:
         self.assertIsInstance(tmpdir, str)
         self.assertEqual(path.realpath(tempfile.gettempdir()), os.getcwd())