tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ini.py (2575B)


      1 # Copyright 2019 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Basic .ini encoding and decoding.
      6 
      7 The basic element in an ini file is the key. Every key is constructed by a name
      8 and a value, delimited by an equals sign (=).
      9 
     10 Keys may be grouped into sections. The secetion name will be a line by itself,
     11 in square brackets ([ and ]). All keys after the section are associated with
     12 that section until another section occurs.
     13 
     14 Keys that are not under any section are considered at the top level.
     15 
     16 Section and key names are case sensitive.
     17 """
     18 
     19 
     20 import contextlib
     21 import os
     22 
     23 
     24 def add_key(line, config, strict=True):
     25  key, val = line.split('=', 1)
     26  key = key.strip()
     27  val = val.strip()
     28  if strict and key in config:
     29    raise ValueError('Multiple entries present for key "%s"' % key)
     30  config[key] = val
     31 
     32 
     33 def loads(ini_str, strict=True):
     34  """Deserialize int_str to a dict (nested dict when has sections) object.
     35 
     36  Duplicated sections will merge their keys.
     37 
     38  When there are multiple entries for a key, at the top level, or under the
     39  same section:
     40   - If strict is true, ValueError will be raised.
     41   - If strict is false, only the last occurrence will be stored.
     42  """
     43  ret = {}
     44  section = None
     45  for line in ini_str.splitlines():
     46    # Empty line
     47    if not line:
     48      continue
     49    # Section line
     50    if line[0] == '[' and line[-1] == ']':
     51      section = line[1:-1]
     52      if section not in ret:
     53        ret[section] = {}
     54    # Key line
     55    else:
     56      config = ret if section is None else ret[section]
     57      add_key(line, config, strict=strict)
     58 
     59  return ret
     60 
     61 
     62 def load(fp):
     63  return loads(fp.read())
     64 
     65 
     66 def dumps(obj):
     67  results = []
     68  key_str = ''
     69 
     70  for k, v in sorted(obj.items()):
     71    if isinstance(v, dict):
     72      results.append('[%s]\n' % k + dumps(v))
     73    else:
     74      key_str += '%s = %s\n' % (k, str(v))
     75 
     76  # Insert key_str at the first position, before any sections
     77  if key_str:
     78    results.insert(0, key_str)
     79 
     80  return '\n'.join(results)
     81 
     82 
     83 def dump(obj, fp):
     84  fp.write(dumps(obj))
     85 
     86 
     87 @contextlib.contextmanager
     88 def update_ini_file(ini_file_path):
     89  """Load and update the contents of an ini file.
     90 
     91  Args:
     92    ini_file_path: A string containing the absolute path of the ini file.
     93  Yields:
     94    The contents of the file, as a dict
     95  """
     96  ini_contents = {}
     97  if os.path.exists(ini_file_path):
     98    with open(ini_file_path) as ini_file:
     99      ini_contents = load(ini_file)
    100 
    101  yield ini_contents
    102 
    103  with open(ini_file_path, 'w') as ini_file:
    104    dump(ini_contents, ini_file)