611 lines
22 KiB
Python
611 lines
22 KiB
Python
# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
"""Gestion des arguments de la ligne de commande.
|
|
"""
|
|
|
|
__all__ = ('split_args', 'join_args', 'build_options', 'get_args',
|
|
'Options',
|
|
)
|
|
|
|
import sys, re
|
|
from getopt import gnu_getopt
|
|
|
|
from .base import isstr, isbool, seqof, odict
|
|
from .output import set_verbosity, VERBOSITY_OPTS
|
|
from .input import set_interaction, INTERACTION_OPTS
|
|
from .functions import apply_args
|
|
|
|
RE_SPACES = re.compile(r'[ \t\r\n]+')
|
|
RE_QUOTE = re.compile(r'"')
|
|
RE_QQUOTE = re.compile(r'\\"')
|
|
RE_SPACES_OR_QUOTES = re.compile(r'[ \t\r\n"]+')
|
|
RE_TOKEN = re.compile(r'[^ \t\r\n"]+')
|
|
RE_QTOKEN = re.compile(r'"((?:\\"|[^"])*)"?')
|
|
|
|
def has_spaces(cl):
|
|
return RE_SPACES.match(cl) is not None
|
|
def skip_spaces(pcl):
|
|
mo = RE_SPACES.match(pcl[0])
|
|
if mo is not None:
|
|
pcl[0] = pcl[0][mo.end(0):]
|
|
def get_token(pcl):
|
|
token = None
|
|
mo = RE_TOKEN.match(pcl[0])
|
|
if mo is not None:
|
|
token, pcl[0] = pcl[0][:mo.end(0)], pcl[0][mo.end(0):]
|
|
return token
|
|
def get_qtoken(pcl):
|
|
qtoken = None
|
|
mo = RE_QTOKEN.match(pcl[0])
|
|
if mo is not None:
|
|
qtoken, pcl[0] = mo.group(1), pcl[0][mo.end(0):]
|
|
return qtoken
|
|
|
|
def split_args(cl):
|
|
"""Lire une chaine, et la découper en plusieurs arguments, à utiliser par
|
|
exemple avec getopt() ou get_args().
|
|
|
|
Note: les arguments peuvent être entre quotes, mais pour le moment, seul "
|
|
est supporté, pas '.
|
|
XXX ajouter le support de ' comme quote.
|
|
|
|
@return: la liste des arguments, ou None si cl==None
|
|
@rtype: list
|
|
"""
|
|
if cl is None: return None
|
|
|
|
args = []
|
|
pcl = [cl]
|
|
while pcl[0]:
|
|
if has_spaces(pcl[0]):
|
|
skip_spaces(pcl)
|
|
if not pcl[0]:
|
|
break
|
|
|
|
arg = ''
|
|
while pcl[0] and not has_spaces(pcl[0]):
|
|
if pcl[0][:1] == '"':
|
|
arg = arg + RE_QQUOTE.sub('"', get_qtoken(pcl))
|
|
else:
|
|
arg = arg + get_token(pcl)
|
|
|
|
args.append(arg)
|
|
|
|
return args
|
|
|
|
def join_args(args):
|
|
"""L'opération inverse de split_args
|
|
|
|
@return: une chaine, ou None si args==None
|
|
"""
|
|
if args is None: return None
|
|
i = 0
|
|
for i in range(len(args)):
|
|
arg = args[i]
|
|
if not args or RE_SPACES_OR_QUOTES.search(arg) is not None:
|
|
args[i] = '"%s"' % RE_QUOTE.sub(r'\"', arg)
|
|
return ' '.join(args)
|
|
|
|
def build_options(argsdesc):
|
|
"""Construire une liste d'options pour utilisation avec get_args ou getopt.
|
|
|
|
A partir d'une liste de termes (option, longoptions, desc), construire et
|
|
retourner (options, longoptions), où options est un chaine et longoptions
|
|
une liste, pour utilisation avec getopt() ou get_args().
|
|
|
|
@return: (options, longoptions)
|
|
@rtype: tuple
|
|
"""
|
|
options = ''
|
|
longoptions = []
|
|
if argsdesc is not None:
|
|
for argdesc in argsdesc:
|
|
if argdesc[0:1] and argdesc[0] is not None:
|
|
options += argdesc[0]
|
|
if argdesc[1:2] and argdesc[1] is not None:
|
|
longopts = argdesc[1]
|
|
if isstr(longopts): longopts = (longopts,)
|
|
longoptions.extend(filter(None, longopts))
|
|
return options, longoptions
|
|
|
|
# options courtes à faire traiter par set_verbosity() ou set_interaction()
|
|
M_OPTIONS = {}
|
|
# options longues à faire traiter par set_verbosity() ou set_interaction()
|
|
M_LONGOPTIONS = {}
|
|
for _opt in VERBOSITY_OPTS:
|
|
if _opt.startswith('--'): M_LONGOPTIONS[_opt] = False
|
|
elif _opt.startswith('-'): M_OPTIONS[_opt] = False
|
|
for _opt in INTERACTION_OPTS:
|
|
if _opt.startswith('--'): M_LONGOPTIONS[_opt] = False
|
|
elif _opt.startswith('-'): M_OPTIONS[_opt] = False
|
|
del _opt
|
|
|
|
RE_OPTION = re.compile(r'.:?')
|
|
def get_args(args=None, options=None, longoptions=None, **optdescs):
|
|
"""frontend pour getopt qui reconnait les options de set_verbosity et
|
|
set_interaction(), et mets à jour les niveaux automatiquement.
|
|
"""
|
|
if args is None: args = sys.argv[1:]
|
|
if options is None: options = ''
|
|
longoptions = seqof(longoptions, [])
|
|
|
|
options = RE_OPTION.findall(options)
|
|
longoptions = list(longoptions)
|
|
|
|
def in_options(opt, options=options):
|
|
"""Retourner True si l'option opt est mentionnée dans options, sans
|
|
tenir compte du fait qu'elle prend ou non un argument dans options.
|
|
|
|
Si opt n'est pas mentionné dans options, l'y rajouter.
|
|
opt doit être de la forme 'o' ou 'o:'
|
|
"""
|
|
normopt = opt[:1]
|
|
for option in options:
|
|
normoption = option[:1]
|
|
if normopt == normoption: return True
|
|
options.append(opt)
|
|
return False
|
|
def in_longoptions(longopt, longoptions=longoptions):
|
|
"""Retourner True si l'option longue longopt est mentionnée dans
|
|
longoptions, sans tenir compte du fait qu'elle prend ou non un argument
|
|
dans longoptions.
|
|
|
|
Si longopt n'est pas mentionné dans longoptions, l'y rajouter.
|
|
longopt doit être de la forme 'longopt' ou 'longopt='
|
|
"""
|
|
if longopt[-1:] == '=': normlongopt = longopt[:-1]
|
|
else: normlongopt = longopt
|
|
for longoption in longoptions:
|
|
if longoption[-1:] == '=': normlongoption = longoption[:-1]
|
|
else: normlongoption = longoption
|
|
if normlongopt == normlongoption: return True
|
|
longoptions.append(longopt)
|
|
return False
|
|
|
|
# déterminer quelles options seront reconnues par set_verbosity. il s'agit
|
|
# de toutes celles qui ne sont pas traitées par l'utilisateur
|
|
m_options = M_OPTIONS.copy()
|
|
m_longoptions = M_LONGOPTIONS.copy()
|
|
|
|
for m_option in m_options.keys():
|
|
# m_option est de la forme '-o'
|
|
if not in_options(m_option[1:]):
|
|
m_options[m_option] = True
|
|
for m_longoption in m_longoptions.keys():
|
|
# m_longoption est de la forme '--longopt'
|
|
if not in_longoptions(m_longoption[2:]):
|
|
m_longoptions[m_longoption] = True
|
|
|
|
# appliquer les options reconnues par set_verbosity
|
|
options = ''.join(options)
|
|
optvalues, args = gnu_getopt(args, options, longoptions)
|
|
for i in range(len(optvalues)):
|
|
opt, _ = optvalues[i]
|
|
set_verbosity_or_interaction = False
|
|
if m_longoptions.get(opt, False): # long options
|
|
set_verbosity_or_interaction = True
|
|
elif m_options.get(opt, False): # options
|
|
set_verbosity_or_interaction = True
|
|
if set_verbosity_or_interaction:
|
|
if opt in VERBOSITY_OPTS:
|
|
set_verbosity(opt)
|
|
elif opt in INTERACTION_OPTS:
|
|
set_interaction(opt)
|
|
optvalues[i] = None
|
|
|
|
# retourner les autres options qui n'ont pas été reconnues
|
|
return filter(None, optvalues), args
|
|
|
|
################################################################################
|
|
|
|
_none = object()
|
|
|
|
RE_PREFIX = re.compile(r'^-*')
|
|
RE_SUFFIX = re.compile(r'[:=]$')
|
|
RE_STUFF = re.compile(r'[^a-zA-Z0-9]')
|
|
def opt2name(opt):
|
|
"""Obtenir un nom de variable dérivé d'un nom d'option
|
|
|
|
Les tirets de début et les caractères : et = de fin sont supprimés, et les
|
|
caractères spéciaux sont remplacés par '_'
|
|
"""
|
|
name = RE_PREFIX.sub('', opt)
|
|
name = RE_SUFFIX.sub('', name)
|
|
name = RE_STUFF.sub('_', name)
|
|
return name
|
|
|
|
class Option(object):
|
|
"""Un objet stockant la description d'une option unique
|
|
|
|
optdef définition de l'option, e.g. 'o', 'o:', 'long-option', ou
|
|
'long-option='
|
|
optname nom de l'option, e.g. 'o' ou 'long-option'
|
|
short est-ce une option courte?
|
|
takes_value
|
|
cette option prend-elle un argument?
|
|
|
|
action action associée à cette option.
|
|
name nom de la variable associée à l'option.
|
|
"""
|
|
|
|
_short, short = None, property(lambda self: self._short)
|
|
_optdef, optdef = None, property(lambda self: self._optdef)
|
|
_optname, optname = None, property(lambda self: self._optname)
|
|
_takes_value, takes_value = None, property(lambda self: self._takes_value)
|
|
|
|
def __init(self, short, optdef, optname, takes_value):
|
|
self._short = short
|
|
self._optdef = optdef
|
|
self._optname = optname
|
|
self._takes_value = takes_value
|
|
|
|
_action, action = None, property(lambda self: self._action)
|
|
_name, name = None, property(lambda self: self._name)
|
|
|
|
LONGOPTION_PATTERN = r'(([a-zA-Z0-9$*@!_][a-zA-Z0-9$*@!_-]*)=?)'
|
|
RE_LONGOPTION0 = re.compile(r'--%s$' % LONGOPTION_PATTERN)
|
|
RE_LONGOPTION1 = re.compile(r'%s$' % LONGOPTION_PATTERN)
|
|
OPTION_PATTERN = r'(([a-zA-Z0-9$*@!_]):?)'
|
|
RE_OPTION0 = re.compile(r'-%s$' % OPTION_PATTERN)
|
|
RE_OPTION1 = re.compile(r'%s$' % OPTION_PATTERN)
|
|
|
|
def __init__(self, optdef):
|
|
if not optdef: raise ValueError("optdef is required")
|
|
|
|
mo = self.RE_LONGOPTION0.match(optdef)
|
|
if mo is not None:
|
|
self.__init(False, mo.group(1), mo.group(2), mo.group(1) != mo.group(2))
|
|
else:
|
|
mo = self.RE_OPTION0.match(optdef)
|
|
if mo is not None:
|
|
self.__init(True, mo.group(1), mo.group(2), mo.group(1) != mo.group(2))
|
|
else:
|
|
mo = self.RE_OPTION1.match(optdef)
|
|
if mo is not None:
|
|
self.__init(True, mo.group(1), mo.group(2), mo.group(1) != mo.group(2))
|
|
else:
|
|
mo = self.RE_LONGOPTION1.match(optdef)
|
|
if mo is not None:
|
|
self.__init(False, mo.group(1), mo.group(2), mo.group(1) != mo.group(2))
|
|
else:
|
|
raise ValueError("Invalid option: %s" % optdef)
|
|
|
|
def __str__(self):
|
|
prefix = self._short and '-' or '--'
|
|
return '%s%s' % (prefix, self._optname)
|
|
str = __str__
|
|
opt = property(__str__)
|
|
|
|
def __repr__(self):
|
|
option = self.__str__()
|
|
if self._takes_value:
|
|
if self._short: option += ':'
|
|
else: option += '='
|
|
return '%s(%s)' % (self.__class__.__name__, repr(option))
|
|
repr = __repr__
|
|
|
|
def same_optdef(self, other):
|
|
return isinstance(other, Option) and self._optdef == other.optdef
|
|
def same_optname(self, other):
|
|
return isinstance(other, Option) and \
|
|
self._optname == other.optname and \
|
|
self._takes_value == other.takes_value
|
|
def __eq__(self, other):
|
|
if isstr(other):
|
|
return self.__str__() == other
|
|
elif isinstance(other, Option):
|
|
return self._optdef == other.optdef
|
|
else:
|
|
return False
|
|
|
|
def set_action(self, action, name=None):
|
|
self._action = action
|
|
self._name = name
|
|
|
|
class Action(object):
|
|
"""Une action associée à une option quand elle est rencontrée sur la ligne
|
|
de commande.
|
|
|
|
name nom de la variable associée à l'option, None s'il faut le calculer
|
|
initial si une valeur est associée à l'option, valeur initiale de cette
|
|
option.
|
|
|
|
Cet objet doit implémenter une méthode __call__() qui prend les arguments
|
|
(option[, value[, options]])
|
|
La méthode doit retourner False si elle veut indiquer qu'elle n'a pas pu
|
|
mettre à jour la valeur. Tout autre valeur indique le succès.
|
|
|
|
option est une instance de Option. value est la valeur associée à l'option,
|
|
ou _none si l'option ne prend pas d'argument. options est l'instance de
|
|
l'objet Options qui analyse les arguments.
|
|
"""
|
|
|
|
name = property(lambda self: None)
|
|
initial = property(lambda self: None)
|
|
|
|
def __call__(self, option=None, value=_none, options=None):
|
|
pass
|
|
|
|
class Options(object):
|
|
"""Une classe permettant de traiter des arguments en ligne de commande.
|
|
|
|
Son objectif est d'offrir une solution plus flexible que les fonctions
|
|
build_options et get_args()
|
|
|
|
Avec le constructeur et la méthode add_option(), il est possible de
|
|
construire la liste des options valides.
|
|
|
|
Ensuite, la méthode parse() permet d'analyser la ligne de commande. Par
|
|
défaut, si une méthode n'est pas définie pour une option, ou si la méthode
|
|
définie retourne False, initialiser une variable nommée d'après l'option, en
|
|
remplaçant sa valeur (si l'option prend un argument) ou lui ajoutant 1 (si
|
|
l'option ne prend pas d'argument).
|
|
"""
|
|
|
|
class SetValue(Action):
|
|
"""Mettre à jour une variable
|
|
|
|
value valeur qu'il faut forcer, ou _none s'il faut prendre la valeur par
|
|
défaut. Si l'option prend un argument, la valeur par défaut est la
|
|
valeur spécifiée sur la ligne de commande. Sinon, il s'agit d'une
|
|
valeur incrémentée représentant le nombre de fois que l'option
|
|
apparait.
|
|
name nom de la variable à initialiser, ou None s'il faut dériver le nom
|
|
de la variable à partir du nom de l'option.
|
|
initial valeur initiale de la variable
|
|
"""
|
|
|
|
_value = None
|
|
_name, name = None, property(lambda self: self._name)
|
|
_initial, initial = None, property(lambda self: self._initial)
|
|
|
|
def __init__(self, value=_none, name=None, initial=None):
|
|
self._value = value
|
|
self._name = name
|
|
self._initial = initial
|
|
|
|
def __call__(self, option=None, value=_none, options=None):
|
|
# nom: celui qui est spécifié dans le constructeur, ou un nom dérivé du
|
|
# nom de l'option
|
|
name = self._name
|
|
if name is None: name = opt2name(option.optname)
|
|
# valeur: celle qui est spécifiée dans le constructeur, ou alors laisser
|
|
# options sans charger
|
|
if self._value is not _none: value = self._value
|
|
|
|
# mettre à jour la valeur
|
|
options.update_value(option, value)
|
|
|
|
class CallMethod(Action):
|
|
_method = None
|
|
|
|
def __init__(self, method=None):
|
|
self._method = method
|
|
|
|
def __call__(self, option=None, value=None, options=None):
|
|
return apply_args(self._method, option, value, options)
|
|
|
|
# type d'analyse: '+' pour s'arrêter à la première non option, '' sinon
|
|
_parseopt = None
|
|
|
|
# liste d'options courtes, instances de Option
|
|
_soptions = None
|
|
|
|
# liste d'options longues, instances de Option
|
|
_loptions = None
|
|
|
|
# valeurs stockées dans cet objet
|
|
_values = None
|
|
|
|
# dictionnaire des options définies, avec chacune une instance de Option
|
|
# associée
|
|
_options = None
|
|
|
|
############################################################################
|
|
# Constructeur
|
|
|
|
def __init__(self, *optdescs):
|
|
"""Initialiser l'objet avec un ensemble d'argument de la forme
|
|
|
|
(options, longoptions, desc)
|
|
|
|
où options est une chaine avec des lettres de la forme 'o' ou 'o:',
|
|
longoptions une liste de chaines de la forme 'option' ou 'option=', et
|
|
desc une chaine quelconque.
|
|
|
|
Ce format est pour assurer la compatibilité avec la fonction
|
|
build_options()
|
|
"""
|
|
super(Options, self).__init__()
|
|
object.__setattr__(self, '_parseopt', '')
|
|
object.__setattr__(self, '_soptions', [])
|
|
object.__setattr__(self, '_loptions', [])
|
|
object.__setattr__(self, '_values', {})
|
|
object.__setattr__(self, '_options', {})
|
|
|
|
self.add_option(VERBOSITY_OPTS, set_verbosity)
|
|
self.add_option(INTERACTION_OPTS, set_interaction)
|
|
for optdesc in optdescs:
|
|
options = filter(None, optdesc[:2])
|
|
desc = optdesc[2:3] and optdesc[2] or None
|
|
self.add_option(options, None, desc)
|
|
|
|
def __option(self, opt):
|
|
"""Obtenir l'instance de Option correspondant à l'argument
|
|
"""
|
|
if isinstance(opt, Option): return opt
|
|
if not opt.startswith('-'):
|
|
if len(opt) == 1: opt = '-' + opt
|
|
else: opt = '--' + opt
|
|
option = self._options.get(opt, None)
|
|
if option is None: raise ValueError("Unknown option: %s" % opt)
|
|
return option
|
|
|
|
def add_option(self, options=None, action=None, desc=None):
|
|
"""Ajouter une option
|
|
|
|
options peut être une chaine de l'une des formes suivantes:
|
|
|
|
'+' arrêter l'analyse à la première non-option (configuration de gnu_getopt)
|
|
'o', '-o', 'o:', '-o:'
|
|
option courte sans et avec argument
|
|
'longo', '--longo', 'longo=', '--longo='
|
|
option longue sans et avec argument
|
|
|
|
options peut aussi être une liste de ces chaines
|
|
"""
|
|
default_name = None
|
|
for opt in filter(None, seqof(options, ())):
|
|
# traiter la configuration de l'analyse '+'
|
|
if opt.startswith('+'):
|
|
self._parseopt = '+'
|
|
opt = opt[1:]
|
|
if not opt: continue
|
|
|
|
# nom par défaut
|
|
if default_name is None:
|
|
default_name = opt2name(opt)
|
|
|
|
# option
|
|
option = Option(opt)
|
|
|
|
# action
|
|
if isinstance(action, Action):
|
|
# action déjà spécifiée
|
|
pass
|
|
elif action is None:
|
|
# pas d'action: mettre à jour la variable d'après le nom de la
|
|
# première option
|
|
action = Options.SetValue(name=default_name)
|
|
elif isstr(action):
|
|
# mettre à jour la variable nommée d'après l'action
|
|
action = Options.SetValue(name=action)
|
|
elif callable(action):
|
|
# appeler l'action
|
|
action = Options.CallMethod(action)
|
|
else:
|
|
raise ValueError("Unsupported action: %s" % repr(action))
|
|
|
|
name = action.name
|
|
if name is None: name = default_name
|
|
|
|
option.set_action(action, name)
|
|
|
|
# si une précédente option est définie, il faut la remplacer
|
|
self._soptions = filter(lambda soption: not soption.same_optname(option), self._soptions)
|
|
self._loptions = filter(lambda loption: not loption.same_optname(option), self._loptions)
|
|
|
|
# nouvelle option
|
|
if option.short: self._soptions.append(option)
|
|
else: self._loptions.append(option)
|
|
self._options[option.opt] = option
|
|
|
|
# valeur initiale
|
|
# ne spécifier la valeur initiale que si elle n'existe pas déjà
|
|
if not self.has_value(option):
|
|
self.set_value(option, action.initial)
|
|
|
|
return self
|
|
|
|
############################################################################
|
|
# Gestion des valeurs
|
|
|
|
def __getitem__(self, key):
|
|
return self._values[key]
|
|
def __setitem__(self, key, value):
|
|
self._values[key] = value
|
|
def __delitem__(self, key):
|
|
del self._values[key]
|
|
def get(self, key, default=None):
|
|
return self._values.get(key, default)
|
|
def __getattr__(self, key, default=_none):
|
|
try:
|
|
if default is _none: return self._values[key]
|
|
else: return self._values.get(key, default)
|
|
except KeyError: raise AttributeError(key)
|
|
def __setattr__(self, key, value):
|
|
if self._values.has_key(key): self._values[key] = value
|
|
else: return super(Options, self).__setattr__(key, value)
|
|
def __delattr__(self, key):
|
|
try: del self._values[key]
|
|
except KeyError: raise AttributeError(key)
|
|
|
|
def get_value(self, option, default=_none):
|
|
"""Obtenir la valeur correspondant à l'option
|
|
"""
|
|
option = self.__option(option)
|
|
return self.get(option.name, default)
|
|
def has_value(self, option):
|
|
option = self.__option(option)
|
|
return self._values.has_key(option.name)
|
|
def set_value(self, option, value):
|
|
"""Spécifier la valeur correspondant à l'option
|
|
"""
|
|
option = self.__option(option)
|
|
self._values[option.name] = value
|
|
return True
|
|
|
|
def update_value(self, option, value=_none):
|
|
option = self.__option(option)
|
|
if value is _none:
|
|
if option.takes_value:
|
|
raise ValueError("Required value")
|
|
else:
|
|
value = self.get_value(option, None)
|
|
if value is None: value = 0
|
|
self.set_value(option, value + 1)
|
|
else:
|
|
self.set_value(option, value)
|
|
|
|
############################################################################
|
|
# Exploitation
|
|
|
|
def get_args(self, args=None):
|
|
"""Analyser les arguments à la recherche des options valides. Si
|
|
args==None, prendre sys.argv[1:]
|
|
|
|
@return (optvalues, args)
|
|
|
|
optvalues est une liste de tuple (opt, value) correspondant à toutes les
|
|
options qui ont été analysées par gnu_getopt(). args est la liste des
|
|
arguments qui ne sont pas des options.
|
|
"""
|
|
if args is None: args = sys.argv[1:]
|
|
soptions = self._parseopt + ''.join([option.optdef for option in self._soptions])
|
|
loptions = [option.optdef for option in self._loptions]
|
|
optvalues, args = gnu_getopt(args, soptions, loptions)
|
|
return filter(None, optvalues), args
|
|
|
|
_parsed_names = None
|
|
|
|
def parse(self, args=None, optvalues=None):
|
|
"""Traiter les options analysées par get_args(). Si optvalues==None,
|
|
analyser les arguments de args avec get_args() d'abord.
|
|
|
|
@return (roptvalues, args)
|
|
|
|
optvalues est une liste de tuple (opt, value) correspondant à toutes les
|
|
options qui ont été analysées, mais n'ont pas pu être traitées par cet
|
|
objet.
|
|
|
|
args est la liste des arguments qui ne sont pas des options.
|
|
"""
|
|
self._parsed_names = {}
|
|
if optvalues is None: optvalues, args = self.get_args(args)
|
|
roptvalues = []
|
|
for opt, value in optvalues:
|
|
option = self.__option(opt)
|
|
self._parsed_names[option.name] = True
|
|
if not option.takes_value: value = _none
|
|
if option.action(option, value, self) == False:
|
|
roptvalues.append((opt, value))
|
|
self.update_value(option, value)
|
|
return roptvalues, args
|
|
|
|
def was_parsed(self, name):
|
|
"""Indiquer si une option correspondant à la variable name a été
|
|
mentionnée sur la ligne de commande.
|
|
"""
|
|
if self._parsed_names is None: return False
|
|
return self._parsed_names.has_key(name)
|