Changeset eaf8222ee65d…
by
Changes to 22 files · Browse files at eaf8222ee65d Diff from another changeset...
|
@@ -0,0 +1,1 @@ + glob:gtk/gtk/*
|
|
@@ -0,0 +1,4 @@ + tortoisehg = http://bitbucket.org/tortoisehg/stable/
+hg-main = http://selenic.com/repo/hg/
+hg-stable = http://selenic.com/repo/hg-stable/
+asciidoc = http://hg.sharesource.org/asciidoc/
|
|
|
|
@@ -0,0 +1,10 @@ + =======================================
+
+add_path.exe was downloaded from http://www.barisione.org/apps.html#add_path
+add_path is a GPL application
+
+=======================================
+
+m*.dll are libraries distributed by Microsoft. They are free for redistribution
+with any Windows applications.
+
|
|
|
@@ -0,0 +1,112 @@ + TortoiseOverlays
+================
+
+Introduction:
+-------------
+Windows only has a limited amount of free slots for icon overlay
+handlers (around 12 - the rest is used by the system itself). This is
+a problem for Tortoise clients because they usually need a lot of those
+handlers to show the different states of files and folders. If a user
+has more than one Tortoise client installed, that's already enough to
+hit the system limit and some overlays won't show up anymore.
+
+The purpose of the TortoiseOverlays project is to provide a common icon
+overlay handler for all the Tortoise clients. This reduces the risk of
+hitting the system limit. The only downside is that all Tortoise clients
+will have the very same overlay icons, which means the users won't
+immediately see from the overlays which version control system is
+handling a certain folder of file. But this is still far better than
+not be able to show an overlay at all because of the system limit.
+
+Using the TortoiseOverlays dll:
+-------------------------------
+Usually, an icon overlay handler is registered under
+HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\ShellIconOverlayIdentifiers
+To make use of the TortoiseOverlays dll, register your overlay handler
+under HKLM\Software\TortoiseOverlays\\Statusname, with 'Statusname'
+being one of:
+Normal under version control, but nothing special
+Modified locally modified, needs committing/pushing
+Conflict requires user interaction to solve a problem
+Deleted item is missing or deleted
+ReadOnly item is readonly, can't be edited
+Locked user has permission to edit item (has exclusive lock)
+Added item is added, but not yet in the repository
+Ignored item is ignored by the version control system
+Unversioned item is not under version control
+
+The difference here is that TortoiseOverlays must know which one of your
+overlay handlers handles which of the states so it can show the correct
+icon. An example:
+instead of creating the registry key
+HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\ShellIconOverlayIdentifiers\TortoiseSVNNormal
+with the "(default)" registry value being the GUID, create the key
+HKLM\Software\TortoiseOverlays\\Normal
+and add a string value "SVN"="GUID"
+
+That's it. No more changes are needed.
+
+How it works:
+-------------
+TortoiseOverlays registers itself with the explorer to handle the nine
+states mentioned above, i.e. it registers nine overlay handlers.
+The explorer process initializes the TortoiseOverlays handler, calling
+its IShellIconOverlayIdentifier::GetOverlayInfo(). TortoiseOverlays
+looks for the registered overlay handlers under
+HKLM\Software\TortoiseOverlays\\Statusname and calls their
+GetOverlayInfo() method so they can initialize too (Note that any change
+to the icon name, index, ... your handler does are overwritten later
+and won't be used - it's TortoiseOverlays that handles the icons now).
+After the initialization, TortoiseOverlays relays every call to its
+IShellIconOverlayIdentifier::IsMemberOf() method to the other handlers.
+The first handler that returns S_OK determines whether the icon is shown
+or not.
+
+Since TortoiseOverlays is shared between all Tortoise clients (it's a
+so called shared component) it installs in
+c:\program files\common files (%commonprogramfiles%) to make sure it's
+available from all clients.
+
+How to install/redistribute TortoiseOverlays:
+---------------------------------------------
+You can either install TortoiseOverlays with the provided merge module
+(which is the recommended way, because it can properly handle the
+required reference counting of the registry keys and dlls), or use the
+also provided msi installer.
+You MUST NOT try to install the dll and create the registry keys yourself!
+If you have a non-msi installer, you can call the msi installer like this:
+msiexec /i TortoiseOverlays-1.0.0.XXXX-win32.msi /qn /norestart
+But in that case, you also must not deinstall it when your client gets
+deinstalled.
+
+The reason I chose merge modules/msi is that TortoiseOverlays is a
+*shared* component, and other setup packages can't handle reference
+counting very well. And removing TortoiseOverlays with your client even
+though some other Tortoise client still needs it is not acceptable.
+
+
+Using custom icons:
+-------------------
+TortoiseOverlays comes with a default set of icons. But if you like to
+give the user the chance to use his own preferred icon set, you can
+tell TortoiseOverlays to use a different set. To do that, create the
+following registry values and set them to the paths of your icons:
+HKCU\Software\TortoiseOverlays\NormalIcon
+HKCU\Software\TortoiseOverlays\ModifiedIcon
+HKCU\Software\TortoiseOverlays\ConflictIcon
+HKCU\Software\TortoiseOverlays\DeletedIcon
+HKCU\Software\TortoiseOverlays\ReadOnlyIcon
+HKCU\Software\TortoiseOverlays\LockedIcon
+HKCU\Software\TortoiseOverlays\AddedIcon
+HKCU\Software\TortoiseOverlays\IgnoredIcon
+HKCU\Software\TortoiseOverlays\UnversionedIcon
+
+Make sure you install your new icon sets under
+c:\program files\common files\TortoiseOverlays\icons\iconsetname
+(%commonprogramfiles%\TortoiseOverlays\icons\iconsetname)
+to avoid missing icons as soon as your client gets uninstalled. Because
+as the TortoiseOverlays dll your icon sets are shared files, shared
+between all Tortoise clients.
+Also make sure your icon sets include *all* icons, not just the ones
+your client uses. Otherwise the user will have inconsistent icons in
+those clients who use all icons.
\ No newline at end of file |
|
@@ -0,0 +1,30 @@ + ====================================================================
+Copyright (c) 2007-2008 The TortoiseSVN Project. All rights reserved.
+
+Preamble:
+The TortoiseOverlays handler was written to reduce the problems with
+the limited available icon overlay handler slots in Windows. The goal
+is to have all clients use it equally. The purpose of this license is
+to allow all clients to use the TortoiseOverlays handler, no matter
+what license the client itself has. And to state some rules for all
+clients to avoid compatibility problems and for users to know where
+the TortoiseOverlays handler comes from.
+
+
+
+Redistribution and use of the TortoiseOverlays handler and the overlay
+icons in it are permitted provided that the following conditions are met:
+
+1. Clients using the TortoiseOverlay handler must provide an
+ acknowlegdement that it was taken from the TortoiseSVN project, with
+ a link to the project website (tortoisesvn.net)
+ in one or more of the following places:
+ a) an "about" box.
+ b) the product user manual.
+ c) a textfile in the installation folder of the application.
+ d) a contributors page on the product web page.
+
+2. The name "TortoiseSVN" must not be used to endorse or
+ promote products using this overlay handler without prior written
+ permission. For written permission, please contact
+ dev@tortoisesvn.tigris.org.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -0,0 +1,9 @@ + You must your GTK+ runtime files into this gtk-dist/ folder.
+
+mkdir thg-winbuild\gtk-dist\gtk
+C:\GTK\bin\*.dll -> thg-winbuild\gtk-dist\gtk\
+C:\GTK\share\themes -> thg-winbuild\gtk-dist\gtk\share\themes
+C:\GTK\lib\pango, gtk-2.0 -> thg-winbuild\gtk-dist\gtk\lib\*
+C:\GTK\etc\fonts, pango, gtk-2.0 -> thg-winbuild\gtk-dist\gtk\etc\*
+
+Also copy whatever locales you want from C:\GTK\share\locale
|
|
@@ -0,0 +1,8 @@ + from ini import INIConfig
+from config import BasicConfig, ConfigNamespace
+from compat import RawConfigParser, ConfigParser, SafeConfigParser
+
+__all__ = [
+ 'INIConfig', 'BasicConfig', 'ConfigNamespace',
+ 'RawConfigParser', 'ConfigParser', 'SafeConfigParser',
+]
|
|
|
@@ -0,0 +1,329 @@ + # Copyright (c) 2001, 2002, 2003 Python Software Foundation
+# Copyright (c) 2004 Paramjit Oberoi <param.cs.wisc.edu>
+# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
+
+"""Compatibility interfaces for ConfigParser
+
+Interfaces of ConfigParser, RawConfigParser and SafeConfigParser
+should be completely identical to the Python standard library
+versions. Tested with the unit tests included with Python-2.3.4
+
+The underlying INIConfig object can be accessed as cfg.data
+"""
+
+import re
+from ConfigParser import DuplicateSectionError, \
+ NoSectionError, NoOptionError, \
+ InterpolationMissingOptionError, \
+ InterpolationDepthError, \
+ InterpolationSyntaxError, \
+ DEFAULTSECT, MAX_INTERPOLATION_DEPTH
+
+# These are imported only for compatiability.
+# The code below does not reference them directly.
+from ConfigParser import Error, InterpolationError, \
+ MissingSectionHeaderError, ParsingError
+
+import ini
+
+class RawConfigParser(object):
+ def __init__(self, defaults=None):
+ self.data = ini.INIConfig(defaults=defaults, optionxformsource=self)
+
+ def optionxform(self, optionstr):
+ return optionstr.lower()
+
+ def defaults(self):
+ d = {}
+ for name, lineobj in self.data._defaults._options:
+ d[name] = lineobj.value
+ return d
+
+ def sections(self):
+ """Return a list of section names, excluding [DEFAULT]"""
+ return list(self.data)
+
+ def add_section(self, section):
+ """Create a new section in the configuration.
+
+ Raise DuplicateSectionError if a section by the specified name
+ already exists.
+ """
+ if self.has_section(section):
+ raise DuplicateSectionError(section)
+ else:
+ self.data.new_namespace(section)
+
+ def has_section(self, section):
+ """Indicate whether the named section is present in the configuration.
+
+ The DEFAULT section is not acknowledged.
+ """
+ try:
+ self.data[section]
+ return True
+ except KeyError:
+ return False
+
+ def options(self, section):
+ """Return a list of option names for the given section name."""
+ try:
+ return list(self.data[section])
+ except KeyError:
+ raise NoSectionError(section)
+
+ def read(self, filenames):
+ """Read and parse a filename or a list of filenames.
+
+ Files that cannot be opened are silently ignored; this is
+ designed so that you can specify a list of potential
+ configuration file locations (e.g. current directory, user's
+ home directory, systemwide directory), and all existing
+ configuration files in the list will be read. A single
+ filename may also be given.
+ """
+ files_read = []
+ if isinstance(filenames, basestring):
+ filenames = [filenames]
+ for filename in filenames:
+ try:
+ fp = open(filename)
+ except IOError:
+ continue
+ files_read.append(filename)
+ self.data.readfp(fp)
+ fp.close()
+ return files_read
+
+ def readfp(self, fp, filename=None):
+ """Like read() but the argument must be a file-like object.
+
+ The `fp' argument must have a `readline' method. Optional
+ second argument is the `filename', which if not given, is
+ taken from fp.name. If fp has no `name' attribute, `<???>' is
+ used.
+ """
+ self.data.readfp(fp)
+
+ def get(self, section, option, vars=None):
+ if not self.has_section(section):
+ raise NoSectionError(section)
+ if vars is not None and option in vars:
+ value = vars[option]
+ try:
+ return self.data[section][option]
+ except KeyError:
+ raise NoOptionError(option, section)
+
+ def items(self, section):
+ try:
+ ans = []
+ for opt in self.data[section]:
+ ans.append((opt, self.data[section][opt]))
+ return ans
+ except KeyError:
+ raise NoSectionError(section)
+
+ def getint(self, section, option):
+ return int(self.get(section, option))
+
+ def getfloat(self, section, option):
+ return float(self.get(section, option))
+
+ _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
+ '0': False, 'no': False, 'false': False, 'off': False}
+
+ def getboolean(self, section, option):
+ v = self.get(section, option)
+ if v.lower() not in self._boolean_states:
+ raise ValueError, 'Not a boolean: %s' % v
+ return self._boolean_states[v.lower()]
+
+ def has_option(self, section, option):
+ """Check for the existence of a given option in a given section."""
+ try:
+ sec = self.data[section]
+ except KeyError:
+ raise NoSectionError(section)
+ try:
+ sec[option]
+ return True
+ except KeyError:
+ return False
+
+ def set(self, section, option, value):
+ """Set an option."""
+ try:
+ self.data[section][option] = value
+ except KeyError:
+ raise NoSectionError(section)
+
+ def write(self, fp):
+ """Write an .ini-format representation of the configuration state."""
+ fp.write(str(self.data))
+
+ def remove_option(self, section, option):
+ """Remove an option."""
+ try:
+ sec = self.data[section]
+ except KeyError:
+ raise NoSectionError(section)
+ try:
+ sec[option]
+ del sec[option]
+ return 1
+ except KeyError:
+ return 0
+
+ def remove_section(self, section):
+ """Remove a file section."""
+ if not self.has_section(section):
+ return False
+ del self.data[section]
+ return True
+
+
+class ConfigDict(object):
+ """Present a dict interface to a ini section."""
+
+ def __init__(self, cfg, section, vars):
+ self.cfg = cfg
+ self.section = section
+ self.vars = vars
+
+ def __getitem__(self, key):
+ try:
+ return RawConfigParser.get(self.cfg, self.section, key, self.vars)
+ except (NoOptionError, NoSectionError):
+ raise KeyError(key)
+
+
+class ConfigParser(RawConfigParser):
+
+ def get(self, section, option, raw=False, vars=None):
+ """Get an option value for a given section.
+
+ All % interpolations are expanded in the return values, based on the
+ defaults passed into the constructor, unless the optional argument
+ `raw' is true. Additional substitutions may be provided using the
+ `vars' argument, which must be a dictionary whose contents overrides
+ any pre-existing defaults.
+
+ The section DEFAULT is special.
+ """
+ if section != DEFAULTSECT and not self.has_section(section):
+ raise NoSectionError(section)
+
+ option = self.optionxform(option)
+ value = RawConfigParser.get(self, section, option, vars)
+
+ if raw:
+ return value
+ else:
+ d = ConfigDict(self, section, vars)
+ return self._interpolate(section, option, value, d)
+
+ def _interpolate(self, section, option, rawval, vars):
+ # do the string interpolation
+ value = rawval
+ depth = MAX_INTERPOLATION_DEPTH
+ while depth: # Loop through this until it's done
+ depth -= 1
+ if "%(" in value:
+ try:
+ value = value % vars
+ except KeyError, e:
+ raise InterpolationMissingOptionError(
+ option, section, rawval, e[0])
+ else:
+ break
+ if value.find("%(") != -1:
+ raise InterpolationDepthError(option, section, rawval)
+ return value
+
+ def items(self, section, raw=False, vars=None):
+ """Return a list of tuples with (name, value) for each option
+ in the section.
+
+ All % interpolations are expanded in the return values, based on the
+ defaults passed into the constructor, unless the optional argument
+ `raw' is true. Additional substitutions may be provided using the
+ `vars' argument, which must be a dictionary whose contents overrides
+ any pre-existing defaults.
+
+ The section DEFAULT is special.
+ """
+ if section != DEFAULTSECT and not self.has_section(section):
+ raise NoSectionError(section)
+ if vars is None:
+ options = list(self.data[section])
+ else:
+ options = []
+ for x in self.data[section]:
+ if x not in vars:
+ options.append(x)
+ options.extend(vars.keys())
+
+ if "__name__" in options:
+ options.remove("__name__")
+
+ d = ConfigDict(self, section, vars)
+ if raw:
+ return [(option, d[option])
+ for option in options]
+ else:
+ return [(option, self._interpolate(section, option, d[option], d))
+ for option in options]
+
+
+class SafeConfigParser(ConfigParser):
+ def set(self, section, option, value):
+ if not isinstance(value, basestring):
+ raise TypeError("option values must be strings")
+ ConfigParser.set(self, section, option, value)
+
+ def _interpolate(self, section, option, rawval, vars):
+ # do the string interpolation
+ L = []
+ self._interpolate_some(option, L, rawval, section, vars, 1)
+ return ''.join(L)
+
+ _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
+
+ def _interpolate_some(self, option, accum, rest, section, map, depth):
+ if depth > MAX_INTERPOLATION_DEPTH:
+ raise InterpolationDepthError(option, section, rest)
+ while rest:
+ p = rest.find("%")
+ if p < 0:
+ accum.append(rest)
+ return
+ if p > 0:
+ accum.append(rest[:p])
+ rest = rest[p:]
+ # p is no longer used
+ c = rest[1:2]
+ if c == "%":
+ accum.append("%")
+ rest = rest[2:]
+ elif c == "(":
+ m = self._interpvar_match(rest)
+ if m is None:
+ raise InterpolationSyntaxError(option, section,
+ "bad interpolation variable reference %r" % rest)
+ var = m.group(1)
+ rest = rest[m.end():]
+ try:
+ v = map[var]
+ except KeyError:
+ raise InterpolationMissingOptionError(
+ option, section, rest, var)
+ if "%" in v:
+ self._interpolate_some(option, accum, v,
+ section, map, depth + 1)
+ else:
+ accum.append(v)
+ else:
+ raise InterpolationSyntaxError(
+ option, section,
+ "'%' must be followed by '%' or '(', found: " + `rest`)
|
|
|
@@ -0,0 +1,264 @@ + # Copyright (c) 2001, 2002, 2003 Python Software Foundation
+# Copyright (c) 2004 Paramjit Oberoi <param.cs.wisc.edu>
+# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
+
+"""Implements basic mechanisms for managing configuration information
+
+* A NAMESPACE is a collection of values and other namepsaces
+* A VALUE is a basic value, like 3.1415, or 'Hello World!'
+* A NAME identifies a value or namespace within a namespace
+
+The ConfigNamespace class is an abstract class that defines the
+basic interface implemented by all config namespace objects. Two
+concrete implementations are included: BasicConfig and INIConfig.
+
+Each is described in detail elsewhere. However, here's an
+example of the capabilities available:
+
+Create config namespace and populate it:
+
+ >>> n = BasicConfig()
+ >>> n.playlist.expand_playlist = True
+ >>> n.ui.display_clock = True
+ >>> n.ui.display_qlength = True
+ >>> n.ui.width = 150
+
+Examine data:
+
+ >>> print n.playlist.expand_playlist
+ True
+ >>> print n['ui']['width']
+ 150
+
+ >>> print n
+ playlist.expand_playlist = True
+ ui.display_clock = True
+ ui.display_qlength = True
+ ui.width = 150
+
+Delete items:
+
+ >>> del n.playlist
+ >>> print n
+ ui.display_clock = True
+ ui.display_qlength = True
+ ui.width = 150
+
+Convert it to ini format:
+
+ >>> from iniparse import ini
+ >>> i = ini.INIConfig()
+ >>> i.import_config(n)
+
+ >>> print i
+ [ui]
+ display_clock = True
+ display_qlength = True
+ width = 150
+"""
+
+# ---- Abstract classes
+
+
+class ConfigNamespace(object):
+ def __getitem__(self, key):
+ return NotImplementedError(key)
+
+ def __setitem__(self, key, value):
+ raise NotImplementedError(key, value)
+
+ def __delitem__(self, key):
+ raise NotImplementedError(key)
+
+ def __iter__(self):
+ return NotImplementedError()
+
+ def new_namespace(self, name):
+ raise NotImplementedError(name)
+
+ def __getattr__(self, name):
+ try:
+ return self.__getitem__(name)
+ except KeyError:
+ return Undefined(name, self)
+
+ def __setattr__(self, name, value):
+ try:
+ object.__getattribute__(self, name)
+ object.__setattr__(self, name, value)
+ return
+ except AttributeError:
+ self.__setitem__(name, value)
+
+ def __delattr__(self, name):
+ try:
+ object.__getattribute__(self, name)
+ object.__delattr__(self, name)
+ except AttributeError:
+ self.__delitem__(name)
+
+ def import_config(self, ns):
+ for name in ns:
+ value = ns[name]
+ if isinstance(value, ConfigNamespace):
+ try:
+ myns = self[name]
+ if not isinstance(myns, ConfigNamespace):
+ raise TypeError('value-namespace conflict')
+ except KeyError:
+ myns = self.new_namespace(name)
+ myns.import_config(value)
+ else:
+ self[name] = value
+
+class Undefined(object):
+ """Helper class used to hold undefined names until assignment.
+
+ This class helps create any undefined subsections when an
+ assignment is made to a nested value. For example, if the
+ statement is "cfg.a.b.c = 42", but "cfg.a.b" does not exist yet.
+ """
+
+ def __init__(self, name, namespace):
+ object.__setattr__(self, 'name', name)
+ object.__setattr__(self, 'namespace', namespace)
+
+ def __setattr__(self, name, value):
+ obj = self.namespace.new_namespace(self.name)
+ obj[name] = value
+
+
+# ---- Basic implementation of namespace
+
+
+class BasicConfig(ConfigNamespace):
+ """Represents a collection of named values
+
+ Values are added using dotted notation:
+
+ >>> n = BasicConfig()
+ >>> n.x = 7
+ >>> n.name.first = 'paramjit'
+ >>> n.name.last = 'oberoi'
+
+ ...and accessed the same way, or with [...]:
+
+ >>> n.x
+ 7
+ >>> n.name.first
+ 'paramjit'
+ >>> n.name.last
+ 'oberoi'
+ >>> n['x']
+ 7
+
+ The namespace object is a 'container object'. The default
+ iterator returns the names of values (i.e. keys).
+
+ >>> l = list(n)
+ >>> l.sort()
+ >>> l
+ ['name', 'x']
+
+ Values can be deleted using 'del' and printed using 'print'.
+
+ >>> n.aaa = 42
+ >>> del n.x
+ >>> print n
+ aaa = 42
+ name.first = paramjit
+ name.last = oberoi
+
+ Nested namepsaces are also namespaces:
+
+ >>> isinstance(n.name, ConfigNamespace)
+ True
+ >>> print n.name
+ first = paramjit
+ last = oberoi
+
+ Finally, values can be read from a file as follows:
+
+ >>> from StringIO import StringIO
+ >>> sio = StringIO('''
+ ... # comment
+ ... ui.height = 100
+ ... ui.width = 150
+ ... complexity = medium
+ ... have_python
+ ... data.secret.password = goodness=gracious me
+ ... ''')
+ >>> n = BasicConfig()
+ >>> n.readfp(sio)
+ >>> print n
+ complexity = medium
+ data.secret.password = goodness=gracious me
+ have_python
+ ui.height = 100
+ ui.width = 150
+ """
+
+ # this makes sure that __setattr__ knows this is not a value key
+ _data = None
+
+ def __init__(self):
+ self._data = {}
+
+ def __getitem__(self, key):
+ return self._data[key]
+
+ def __setitem__(self, key, value):
+ self._data[key] = value
+
+ def __delitem__(self, key):
+ del self._data[key]
+
+ def __iter__(self):
+ return iter(self._data)
+
+ def __str__(self, prefix=''):
+ lines = []
+ keys = self._data.keys()
+ keys.sort()
+ for name in keys:
+ value = self._data[name]
+ if isinstance(value, ConfigNamespace):
+ lines.append(value.__str__(prefix='%s%s.' % (prefix,name)))
+ else:
+ if value is None:
+ lines.append('%s%s' % (prefix, name))
+ else:
+ lines.append('%s%s = %s' % (prefix, name, value))
+ return '\n'.join(lines)
+
+ def new_namespace(self, name):
+ obj = BasicConfig()
+ self._data[name] = obj
+ return obj
+
+ def readfp(self, fp):
+ while True:
+ line = fp.readline()
+ if not line:
+ break
+
+ line = line.strip()
+ if not line: continue
+ if line[0] == '#': continue
+ data = line.split('=', 1)
+ if len(data) == 1:
+ name = line
+ value = None
+ else:
+ name = data[0].strip()
+ value = data[1].strip()
+ name_components = name.split('.')
+ ns = self
+ for n in name_components[:-1]:
+ try:
+ ns = ns[n]
+ if not isinstance(ns, ConfigNamespace):
+ raise TypeError('value-namespace conflict', n)
+ except KeyError:
+ ns = ns.new_namespace(n)
+ ns[name_components[-1]] = value
|
|
|
@@ -0,0 +1,568 @@ + # Copyright (c) 2001, 2002, 2003 Python Software Foundation
+# Copyright (c) 2004 Paramjit Oberoi <param.cs.wisc.edu>
+# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
+
+"""Access and/or modify INI files
+
+* Compatiable with ConfigParser
+* Preserves order of sections & options
+* Preserves comments/blank lines/etc
+* More conveninet access to data
+
+Example:
+
+ >>> from StringIO import StringIO
+ >>> sio = StringIO('''# configure foo-application
+ ... [foo]
+ ... bar1 = qualia
+ ... bar2 = 1977
+ ... [foo-ext]
+ ... special = 1''')
+
+ >>> cfg = INIConfig(sio)
+ >>> print cfg.foo.bar1
+ qualia
+ >>> print cfg['foo-ext'].special
+ 1
+ >>> cfg.foo.newopt = 'hi!'
+
+ >>> print cfg
+ # configure foo-application
+ [foo]
+ bar1 = qualia
+ bar2 = 1977
+ newopt = hi!
+ [foo-ext]
+ special = 1
+
+"""
+
+# An ini parser that supports ordered sections/options
+# Also supports updates, while preserving structure
+# Backward-compatiable with ConfigParser
+
+import re
+import config
+from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError
+
+class LineType(object):
+ line = None
+
+ def __init__(self, line=None):
+ if line is not None:
+ self.line = line.strip('\n')
+
+ # Return the original line for unmodified objects
+ # Otherwise construct using the current attribute values
+ def __str__(self):
+ if self.line is not None:
+ return self.line
+ else:
+ return self.to_string()
+
+ # If an attribute is modified after initialization
+ # set line to None since it is no longer accurate.
+ def __setattr__(self, name, value):
+ if hasattr(self,name):
+ self.__dict__['line'] = None
+ self.__dict__[name] = value
+
+ def to_string(self):
+ raise Exception('This method must be overridden in derived classes')
+
+
+class SectionLine(LineType):
+ regex = re.compile(r'^\['
+ r'(?P<name>[^]]+)'
+ r'\]\s*'
+ r'((?P<csep>;|#)(?P<comment>.*))?$')
+
+ def __init__(self, name, comment=None, comment_separator=None,
+ comment_offset=-1, line=None):
+ super(SectionLine, self).__init__(line)
+ self.name = name
+ self.comment = comment
+ self.comment_separator = comment_separator
+ self.comment_offset = comment_offset
+
+ def to_string(self):
+ out = '[' + self.name + ']'
+ if self.comment is not None:
+ # try to preserve indentation of comments
+ out = (out+' ').ljust(self.comment_offset)
+ out = out + self.comment_separator + self.comment
+ return out
+
+ def parse(cls, line):
+ m = cls.regex.match(line.rstrip())
+ if m is None:
+ return None
+ return cls(m.group('name'), m.group('comment'),
+ m.group('csep'), m.start('csep'),
+ line)
+ parse = classmethod(parse)
+
+
+class OptionLine(LineType):
+ def __init__(self, name, value, separator=' = ', comment=None,
+ comment_separator=None, comment_offset=-1, line=None):
+ super(OptionLine, self).__init__(line)
+ self.name = name
+ self.value = value
+ self.separator = separator
+ self.comment = comment
+ self.comment_separator = comment_separator
+ self.comment_offset = comment_offset
+
+ def to_string(self):
+ out = '%s%s%s' % (self.name, self.separator, self.value)
+ if self.comment is not None:
+ # try to preserve indentation of comments
+ out = (out+' ').ljust(self.comment_offset)
+ out = out + self.comment_separator + self.comment
+ return out
+
+ regex = re.compile(r'^(?P<name>[^:=\s[][^:=]*)'
+ r'(?P<sep>[:=]\s*)'
+ r'(?P<value>.*)$')
+
+ def parse(cls, line):
+ m = cls.regex.match(line.rstrip())
+ if m is None:
+ return None
+
+ name = m.group('name').rstrip()
+ value = m.group('value')
+ sep = m.group('name')[len(name):] + m.group('sep')
+
+ # comments are not detected in the regex because
+ # ensuring total compatibility with ConfigParser
+ # requires that:
+ # option = value ;comment // value=='value'
+ # option = value;1 ;comment // value=='value;1 ;comment'
+ #
+ # Doing this in a regex would be complicated. I
+ # think this is a bug. The whole issue of how to
+ # include ';' in the value needs to be addressed.
+ # Also, '#' doesn't mark comments in options...
+
+ coff = value.find(';')
+ if coff != -1 and value[coff-1].isspace():
+ comment = value[coff+1:]
+ csep = value[coff]
+ value = value[:coff].rstrip()
+ coff = m.start('value') + coff
+ else:
+ comment = None
+ csep = None
+ coff = -1
+
+ return cls(name, value, sep, comment, csep, coff, line)
+ parse = classmethod(parse)
+
+
+class CommentLine(LineType):
+ regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])'
+ r'(?P<comment>.*)$')
+
+ def __init__(self, comment='', separator='#', line=None):
+ super(CommentLine, self).__init__(line)
+ self.comment = comment
+ self.separator = separator
+
+ def to_string(self):
+ return self.separator + self.comment
+
+ def parse(cls, line):
+ m = cls.regex.match(line.rstrip())
+ if m is None:
+ return None
+ return cls(m.group('comment'), m.group('csep'), line)
+ parse = classmethod(parse)
+
+
+class EmptyLine(LineType):
+ # could make this a singleton
+ def to_string(self):
+ return ''
+
+ def parse(cls, line):
+ if line.strip(): return None
+ return cls(line)
+ parse = classmethod(parse)
+
+
+class ContinuationLine(LineType):
+ regex = re.compile(r'^\s+(?P<value>.*)$')
+
+ def __init__(self, value, value_offset=8, line=None):
+ super(ContinuationLine, self).__init__(line)
+ self.value = value
+ self.value_offset = value_offset
+
+ def to_string(self):
+ return ' '*self.value_offset + self.value
+
+ def parse(cls, line):
+ m = cls.regex.match(line.rstrip())
+ if m is None:
+ return None
+ return cls(m.group('value'), m.start('value'), line)
+ parse = classmethod(parse)
+
+
+class LineContainer(object):
+ def __init__(self, d=None):
+ self.contents = []
+ self.orgvalue = None
+ if d:
+ if isinstance(d, list): self.extend(d)
+ else: self.add(d)
+
+ def add(self, x):
+ self.contents.append(x)
+
+ def extend(self, x):
+ for i in x: self.add(i)
+
+ def get_name(self):
+ return self.contents[0].name
+
+ def set_name(self, data):
+ self.contents[0].name = data
+
+ def get_value(self):
+ if self.orgvalue is not None:
+ return self.orgvalue
+ elif len(self.contents) == 1:
+ return self.contents[0].value
+ else:
+ return '\n'.join([str(x.value) for x in self.contents
+ if not isinstance(x, (CommentLine, EmptyLine))])
+
+ def set_value(self, data):
+ self.orgvalue = data
+ lines = str(data).split('\n')
+ linediff = len(lines) - len(self.contents)
+ if linediff > 0:
+ for _ in range(linediff):
+ self.add(ContinuationLine(''))
+ elif linediff < 0:
+ self.contents = self.contents[:linediff]
+ for i,v in enumerate(lines):
+ self.contents[i].value = v
+
+ name = property(get_name, set_name)
+ value = property(get_value, set_value)
+
+ def __str__(self):
+ s = [str(x) for x in self.contents]
+ return '\n'.join(s)
+
+ def finditer(self, key):
+ for x in self.contents[::-1]:
+ if hasattr(x, 'name') and x.name==key:
+ yield x
+
+ def find(self, key):
+ for x in self.finditer(key):
+ return x
+ raise KeyError(key)
+
+
+def _make_xform_property(myattrname, srcattrname=None):
+ private_attrname = myattrname + 'value'
+ private_srcname = myattrname + 'source'
+ if srcattrname is None:
+ srcattrname = myattrname
+
+ def getfn(self):
+ srcobj = getattr(self, private_srcname)
+ if srcobj is not None:
+ return getattr(srcobj, srcattrname)
+ else:
+ return getattr(self, private_attrname)
+
+ def setfn(self, value):
+ srcobj = getattr(self, private_srcname)
+ if srcobj is not None:
+ setattr(srcobj, srcattrname, value)
+ else:
+ setattr(self, private_attrname, value)
+
+ return property(getfn, setfn)
+
+
+class INISection(config.ConfigNamespace):
+ _lines = None
+ _options = None
+ _defaults = None
+ _optionxformvalue = None
+ _optionxformsource = None
+ def __init__(self, lineobj, defaults = None,
+ optionxformvalue=None, optionxformsource=None):
+ self._lines = [lineobj]
+ self._defaults = defaults
+ self._optionxformvalue = optionxformvalue
+ self._optionxformsource = optionxformsource
+ self._options = {}
+
+ _optionxform = _make_xform_property('_optionxform')
+
+ def __getitem__(self, key):
+ if key == '__name__':
+ return self._lines[-1].name
+ if self._optionxform: key = self._optionxform(key)
+ try:
+ return self._options[key].value
+ except KeyError:
+ if self._defaults and key in self._defaults._options:
+ return self._defaults._options[key].value
+ else:
+ raise
+
+ def __setitem__(self, key, value):
+ if self._optionxform: xkey = self._optionxform(key)
+ else: xkey = key
+ if xkey not in self._options:
+ # create a dummy object - value may have multiple lines
+ obj = LineContainer(OptionLine(key, ''))
+ self._lines[-1].add(obj)
+ self._options[xkey] = obj
+ # the set_value() function in LineContainer
+ # automatically handles multi-line values
+ self._options[xkey].value = value
+
+ def __delitem__(self, key):
+ if self._optionxform: key = self._optionxform(key)
+ for l in self._lines:
+ remaining = []
+ for o in l.contents:
+ if isinstance(o, LineContainer):
+ n = o.name
+ if self._optionxform: n = self._optionxform(n)
+ if key != n: remaining.append(o)
+ else:
+ remaining.append(o)
+ l.contents = remaining
+ del self._options[key]
+
+ def __iter__(self):
+ d = set()
+ for l in self._lines:
+ for x in l.contents:
+ if isinstance(x, LineContainer):
+ if self._optionxform:
+ ans = self._optionxform(x.name)
+ else:
+ ans = x.name
+ if ans not in d:
+ yield ans
+ d.add(ans)
+ if self._defaults:
+ for x in self._defaults:
+ if x not in d:
+ yield x
+ d.add(x)
+
+ def new_namespace(self, name):
+ raise Exception('No sub-sections allowed', name)
+
+
+def make_comment(line):
+ return CommentLine(line.rstrip())
+
+
+def readline_iterator(f):
+ """iterate over a file by only using the file object's readline method"""
+
+ have_newline = False
+ while True:
+ line = f.readline()
+
+ if not line:
+ if have_newline:
+ yield ""
+ return
+
+ if line.endswith('\n'):
+ have_newline = True
+ else:
+ have_newline = False
+
+ yield line
+
+
+class INIConfig(config.ConfigNamespace):
+ _data = None
+ _sections = None
+ _defaults = None
+ _optionxformvalue = None
+ _optionxformsource = None
+ _sectionxformvalue = None
+ _sectionxformsource = None
+ _parse_exc = None
+ def __init__(self, fp=None, defaults = None, parse_exc=True,
+ optionxformvalue=str.lower, optionxformsource=None,
+ sectionxformvalue=None, sectionxformsource=None):
+ self._data = LineContainer()
+ self._parse_exc = parse_exc
+ self._optionxformvalue = optionxformvalue
+ self._optionxformsource = optionxformsource
+ self._sectionxformvalue = sectionxformvalue
+ self._sectionxformsource = sectionxformsource
+ self._sections = {}
+ if defaults is None: defaults = {}
+ self._defaults = INISection(LineContainer(), optionxformsource=self)
+ for name, value in defaults.iteritems():
+ self._defaults[name] = value
+ if fp is not None:
+ self.readfp(fp)
+
+ _optionxform = _make_xform_property('_optionxform', 'optionxform')
+ _sectionxform = _make_xform_property('_sectionxform', 'optionxform')
+
+ def __getitem__(self, key):
+ if key == DEFAULTSECT:
+ return self._defaults
+ if self._sectionxform: key = self._sectionxform(key)
+ return self._sections[key]
+
+ def __setitem__(self, key, value):
+ raise Exception('Values must be inside sections', key, value)
+
+ def __delitem__(self, key):
+ if self._sectionxform: key = self._sectionxform(key)
+ for line in self._sections[key]._lines:
+ self._data.contents.remove(line)
+ del self._sections[key]
+
+ def __iter__(self):
+ d = set()
+ for x in self._data.contents:
+ if isinstance(x, LineContainer):
+ if x.name not in d:
+ yield x.name
+ d.add(x.name)
+
+ def new_namespace(self, name):
+ if self._data.contents:
+ self._data.add(EmptyLine())
+ obj = LineContainer(SectionLine(name))
+ self._data.add(obj)
+ if self._sectionxform: name = self._sectionxform(name)
+ if name in self._sections:
+ ns = self._sections[name]
+ ns._lines.append(obj)
+ else:
+ ns = INISection(obj, defaults=self._defaults,
+ optionxformsource=self)
+ self._sections[name] = ns
+ return ns
+
+ def __str__(self):
+ return str(self._data)
+
+ _line_types = [EmptyLine, CommentLine,
+ SectionLine, OptionLine,
+ ContinuationLine]
+
+ def _parse(self, line):
+ for linetype in self._line_types:
+ lineobj = linetype.parse(line)
+ if lineobj:
+ return lineobj
+ else:
+ # can't parse line
+ return None
+
+ def readfp(self, fp):
+ cur_section = None
+ cur_option = None
+ cur_section_name = None
+ cur_option_name = None
+ pending_lines = []
+ try:
+ fname = fp.name
+ except AttributeError:
+ fname = '<???>'
+ linecount = 0
+ exc = None
+ line = None
+
+ for line in readline_iterator(fp):
+ lineobj = self._parse(line)
+ linecount += 1
+
+ if not cur_section and not isinstance(lineobj,
+ (CommentLine, EmptyLine, SectionLine)):
+ if self._parse_exc:
+ raise MissingSectionHeaderError(fname, linecount, line)
+ else:
+ lineobj = make_comment(line)
+
+ if lineobj is None:
+ if self._parse_exc:
+ if exc is None: exc = ParsingError(fname)
+ exc.append(linecount, line)
+ lineobj = make_comment(line)
+
+ if isinstance(lineobj, ContinuationLine):
+ if cur_option:
+ cur_option.extend(pending_lines)
+ pending_lines = []
+ cur_option.add(lineobj)
+ else:
+ # illegal continuation line - convert to comment
+ if self._parse_exc:
+ if exc is None: exc = ParsingError(fname)
+ exc.append(linecount, line)
+ lineobj = make_comment(line)
+
+ if isinstance(lineobj, OptionLine):
+ cur_section.extend(pending_lines)
+ pending_lines = []
+ cur_option = LineContainer(lineobj)
+ cur_section.add(cur_option)
+ if self._optionxform:
+ cur_option_name = self._optionxform(cur_option.name)
+ else:
+ cur_option_name = cur_option.name
+ if cur_section_name == DEFAULTSECT:
+ optobj = self._defaults
+ else:
+ optobj = self._sections[cur_section_name]
+ optobj._options[cur_option_name] = cur_option
+
+ if isinstance(lineobj, SectionLine):
+ self._data.extend(pending_lines)
+ pending_lines = []
+ cur_section = LineContainer(lineobj)
+ self._data.add(cur_section)
+ cur_option = None
+ cur_option_name = None
+ if cur_section.name == DEFAULTSECT:
+ self._defaults._lines.append(cur_section)
+ cur_section_name = DEFAULTSECT
+ else:
+ if self._sectionxform:
+ cur_section_name = self._sectionxform(cur_section.name)
+ else:
+ cur_section_name = cur_section.name
+ if not self._sections.has_key(cur_section_name):
+ self._sections[cur_section_name] = \
+ INISection(cur_section, defaults=self._defaults,
+ optionxformsource=self)
+ else:
+ self._sections[cur_section_name]._lines.append(cur_section)
+
+ if isinstance(lineobj, (CommentLine, EmptyLine)):
+ pending_lines.append(lineobj)
+
+ self._data.extend(pending_lines)
+ if line and line[-1]=='\n':
+ self._data.add(EmptyLine())
+
+ if exc:
+ raise exc
+
|
|
|
|
|
|
|
|
@@ -0,0 +1,6 @@ + [py2exe]
+excludes=pywin,pywin.dialogs,pywin.dialogs.list,svn
+packages=email,hgext,hgext.convert,encodings
+
+[build]
+compiler=mingw32
|
Loading...