PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/nltk/internals.py

https://github.com/haewoon/nltk
Python | 864 lines | 808 code | 13 blank | 43 comment | 17 complexity | 93bdec6fa098b5a8786a85f6e78f3f48 MD5 | raw file
Possible License(s): Apache-2.0
  1. # Natural Language Toolkit: Internal utility functions
  2. #
  3. # Copyright (C) 2001-2012 NLTK Project
  4. # Author: Steven Bird <sb@csse.unimelb.edu.au>
  5. # Edward Loper <edloper@gradient.cis.upenn.edu>
  6. # Nitin Madnani <nmadnani@ets.org>
  7. # URL: <http://www.nltk.org/>
  8. # For license information, see LICENSE.TXT
  9. import subprocess
  10. import os
  11. import os.path
  12. import re
  13. import warnings
  14. import textwrap
  15. import types
  16. import sys
  17. import stat
  18. # Use the c version of ElementTree, which is faster, if possible:
  19. try: from xml.etree import cElementTree as ElementTree
  20. except ImportError: from xml.etree import ElementTree
  21. from nltk import __file__
  22. ######################################################################
  23. # Regular Expression Processing
  24. ######################################################################
  25. def convert_regexp_to_nongrouping(pattern):
  26. """
  27. Convert all grouping parentheses in the given regexp pattern to
  28. non-grouping parentheses, and return the result. E.g.:
  29. >>> from nltk.internals import convert_regexp_to_nongrouping
  30. >>> convert_regexp_to_nongrouping('ab(c(x+)(z*))?d')
  31. 'ab(?:c(?:x+)(?:z*))?d'
  32. :type pattern: str
  33. :rtype: str
  34. """
  35. # Sanity check: back-references are not allowed!
  36. for s in re.findall(r'\\.|\(\?P=', pattern):
  37. if s[1] in '0123456789' or s == '(?P=':
  38. raise ValueError('Regular expressions with back-references '
  39. 'are not supported: %r' % pattern)
  40. # This regexp substitution function replaces the string '('
  41. # with the string '(?:', but otherwise makes no changes.
  42. def subfunc(m):
  43. return re.sub('^\((\?P<[^>]*>)?$', '(?:', m.group())
  44. # Scan through the regular expression. If we see any backslashed
  45. # characters, ignore them. If we see a named group, then
  46. # replace it with "(?:". If we see any open parens that are part
  47. # of an extension group, ignore those too. But if we see
  48. # any other open paren, replace it with "(?:")
  49. return re.sub(r'''(?x)
  50. \\. | # Backslashed character
  51. \(\?P<[^>]*> | # Named group
  52. \(\? | # Extension group
  53. \( # Grouping parenthesis''', subfunc, pattern)
  54. ##########################################################################
  55. # Java Via Command-Line
  56. ##########################################################################
  57. _java_bin = None
  58. _java_options = []
  59. # [xx] add classpath option to config_java?
  60. def config_java(bin=None, options=None, verbose=True):
  61. """
  62. Configure nltk's java interface, by letting nltk know where it can
  63. find the Java binary, and what extra options (if any) should be
  64. passed to Java when it is run.
  65. :param bin: The full path to the Java binary. If not specified,
  66. then nltk will search the system for a Java binary; and if
  67. one is not found, it will raise a ``LookupError`` exception.
  68. :type bin: str
  69. :param options: A list of options that should be passed to the
  70. Java binary when it is called. A common value is
  71. ``'-Xmx512m'``, which tells Java binary to increase
  72. the maximum heap size to 512 megabytes. If no options are
  73. specified, then do not modify the options list.
  74. :type options: list(str)
  75. """
  76. global _java_bin, _java_options
  77. _java_bin = find_binary('java', bin, env_vars=['JAVAHOME', 'JAVA_HOME'], verbose=verbose)
  78. if options is not None:
  79. if isinstance(options, basestring):
  80. options = options.split()
  81. _java_options = list(options)
  82. def java(cmd, classpath=None, stdin=None, stdout=None, stderr=None,
  83. blocking=True):
  84. """
  85. Execute the given java command, by opening a subprocess that calls
  86. Java. If java has not yet been configured, it will be configured
  87. by calling ``config_java()`` with no arguments.
  88. :param cmd: The java command that should be called, formatted as
  89. a list of strings. Typically, the first string will be the name
  90. of the java class; and the remaining strings will be arguments
  91. for that java class.
  92. :type cmd: list(str)
  93. :param classpath: A ``':'`` separated list of directories, JAR
  94. archives, and ZIP archives to search for class files.
  95. :type classpath: str
  96. :param stdin, stdout, stderr: Specify the executed programs'
  97. standard input, standard output and standard error file
  98. handles, respectively. Valid values are ``subprocess.PIPE``,
  99. an existing file descriptor (a positive integer), an existing
  100. file object, and None. ``subprocess.PIPE`` indicates that a
  101. new pipe to the child should be created. With None, no
  102. redirection will occur; the child's file handles will be
  103. inherited from the parent. Additionally, stderr can be
  104. ``subprocess.STDOUT``, which indicates that the stderr data
  105. from the applications should be captured into the same file
  106. handle as for stdout.
  107. :param blocking: If ``false``, then return immediately after
  108. spawning the subprocess. In this case, the return value is
  109. the ``Popen`` object, and not a ``(stdout, stderr)`` tuple.
  110. :return: If ``blocking=True``, then return a tuple ``(stdout,
  111. stderr)``, containing the stdout and stderr outputs generated
  112. by the java command if the ``stdout`` and ``stderr`` parameters
  113. were set to ``subprocess.PIPE``; or None otherwise. If
  114. ``blocking=False``, then return a ``subprocess.Popen`` object.
  115. :raise OSError: If the java command returns a nonzero return code.
  116. """
  117. if stdin == 'pipe': stdin = subprocess.PIPE
  118. if stdout == 'pipe': stdout = subprocess.PIPE
  119. if stderr == 'pipe': stderr = subprocess.PIPE
  120. if isinstance(cmd, basestring):
  121. raise TypeError('cmd should be a list of strings')
  122. # Make sure we know where a java binary is.
  123. if _java_bin is None:
  124. config_java()
  125. # Set up the classpath.
  126. if classpath is None:
  127. classpath = NLTK_JAR
  128. else:
  129. classpath += os.path.pathsep + NLTK_JAR
  130. # Construct the full command string.
  131. cmd = list(cmd)
  132. cmd = ['-cp', classpath] + cmd
  133. cmd = [_java_bin] + _java_options + cmd
  134. # Call java via a subprocess
  135. p = subprocess.Popen(cmd, stdin=stdin, stdout=stdout, stderr=stderr)
  136. if not blocking: return p
  137. (stdout, stderr) = p.communicate()
  138. # Check the return code.
  139. if p.returncode != 0:
  140. print stderr
  141. raise OSError('Java command failed!')
  142. return (stdout, stderr)
  143. #: The location of the NLTK jar file, which is used to communicate
  144. #: with external Java packages (such as Mallet) that do not have
  145. #: a sufficiently powerful native command-line interface.
  146. NLTK_JAR = os.path.abspath(os.path.join(os.path.split(__file__)[0],
  147. 'nltk.jar'))
  148. if 0:
  149. #config_java(options='-Xmx512m')
  150. # Write:
  151. #java('weka.classifiers.bayes.NaiveBayes',
  152. # ['-d', '/tmp/names.model', '-t', '/tmp/train.arff'],
  153. # classpath='/Users/edloper/Desktop/weka/weka.jar')
  154. # Read:
  155. (a,b) = java(['weka.classifiers.bayes.NaiveBayes',
  156. '-l', '/tmp/names.model', '-T', '/tmp/test.arff',
  157. '-p', '0'],#, '-distribution'],
  158. classpath='/Users/edloper/Desktop/weka/weka.jar')
  159. ######################################################################
  160. # Parsing
  161. ######################################################################
  162. class ParseError(ValueError):
  163. """
  164. Exception raised by parse_* functions when they fail.
  165. :param position: The index in the input string where an error occurred.
  166. :param expected: What was expected when an error occurred.
  167. """
  168. def __init__(self, expected, position):
  169. ValueError.__init__(self, expected, position)
  170. self.expected = expected
  171. self.position = position
  172. def __str__(self):
  173. return 'Expected %s at %s' % (self.expected, self.position)
  174. _STRING_START_RE = re.compile(r"[uU]?[rR]?(\"\"\"|\'\'\'|\"|\')")
  175. def parse_str(s, start_position):
  176. """
  177. If a Python string literal begins at the specified position in the
  178. given string, then return a tuple ``(val, end_position)``
  179. containing the value of the string literal and the position where
  180. it ends. Otherwise, raise a ``ParseError``.
  181. """
  182. # Read the open quote, and any modifiers.
  183. m = _STRING_START_RE.match(s, start_position)
  184. if not m: raise ParseError('open quote', start_position)
  185. quotemark = m.group(1)
  186. # Find the close quote.
  187. _STRING_END_RE = re.compile(r'\\|%s' % quotemark)
  188. position = m.end()
  189. while True:
  190. match = _STRING_END_RE.search(s, position)
  191. if not match: raise ParseError('close quote', position)
  192. if match.group(0) == '\\': position = match.end()+1
  193. else: break
  194. # Parse it, using eval. Strings with invalid escape sequences
  195. # might raise ValueEerror.
  196. try:
  197. return eval(s[start_position:match.end()]), match.end()
  198. except ValueError, e:
  199. raise ParseError('valid string (%s)' % e, start)
  200. _PARSE_INT_RE = re.compile(r'-?\d+')
  201. def parse_int(s, start_position):
  202. """
  203. If an integer begins at the specified position in the given
  204. string, then return a tuple ``(val, end_position)`` containing the
  205. value of the integer and the position where it ends. Otherwise,
  206. raise a ``ParseError``.
  207. """
  208. m = _PARSE_INT_RE.match(s, start_position)
  209. if not m: raise ParseError('integer', start_position)
  210. return int(m.group()), m.end()
  211. _PARSE_NUMBER_VALUE = re.compile(r'-?(\d*)([.]?\d*)?')
  212. def parse_number(s, start_position):
  213. """
  214. If an integer or float begins at the specified position in the
  215. given string, then return a tuple ``(val, end_position)``
  216. containing the value of the number and the position where it ends.
  217. Otherwise, raise a ``ParseError``.
  218. """
  219. m = _PARSE_NUMBER_VALUE.match(s, start_position)
  220. if not m or not (m.group(1) or m.group(2)):
  221. raise ParseError('number', start_position)
  222. if m.group(2): return float(m.group()), m.end()
  223. else: return int(m.group()), m.end()
  224. ######################################################################
  225. # Check if a method has been overridden
  226. ######################################################################
  227. def overridden(method):
  228. """
  229. :return: True if ``method`` overrides some method with the same
  230. name in a base class. This is typically used when defining
  231. abstract base classes or interfaces, to allow subclasses to define
  232. either of two related methods:
  233. >>> class EaterI:
  234. ... '''Subclass must define eat() or batch_eat().'''
  235. ... def eat(self, food):
  236. ... if overridden(self.batch_eat):
  237. ... return self.batch_eat([food])[0]
  238. ... else:
  239. ... raise NotImplementedError()
  240. ... def batch_eat(self, foods):
  241. ... return [self.eat(food) for food in foods]
  242. :type method: instance method
  243. """
  244. # [xx] breaks on classic classes!
  245. if isinstance(method, types.MethodType) and method.im_class is not None:
  246. name = method.__name__
  247. funcs = [cls.__dict__[name]
  248. for cls in _mro(method.im_class)
  249. if name in cls.__dict__]
  250. return len(funcs) > 1
  251. else:
  252. raise TypeError('Expected an instance method.')
  253. def _mro(cls):
  254. """
  255. Return the method resolution order for ``cls`` -- i.e., a list
  256. containing ``cls`` and all its base classes, in the order in which
  257. they would be checked by ``getattr``. For new-style classes, this
  258. is just cls.__mro__. For classic classes, this can be obtained by
  259. a depth-first left-to-right traversal of ``__bases__``.
  260. """
  261. if isinstance(cls, type):
  262. return cls.__mro__
  263. else:
  264. mro = [cls]
  265. for base in cls.__bases__: mro.extend(_mro(base))
  266. return mro
  267. ######################################################################
  268. # Deprecation decorator & base class
  269. ######################################################################
  270. # [xx] dedent msg first if it comes from a docstring.
  271. def _add_epytext_field(obj, field, message):
  272. """Add an epytext @field to a given object's docstring."""
  273. indent = ''
  274. # If we already have a docstring, then add a blank line to separate
  275. # it from the new field, and check its indentation.
  276. if obj.__doc__:
  277. obj.__doc__ = obj.__doc__.rstrip()+'\n\n'
  278. indents = re.findall(r'(?<=\n)[ ]+(?!\s)', obj.__doc__.expandtabs())
  279. if indents: indent = min(indents)
  280. # If we don't have a docstring, add an empty one.
  281. else:
  282. obj.__doc__ = ''
  283. obj.__doc__ += textwrap.fill('@%s: %s' % (field, message),
  284. initial_indent=indent,
  285. subsequent_indent=indent+' ')
  286. def deprecated(message):
  287. """
  288. A decorator used to mark functions as deprecated. This will cause
  289. a warning to be printed the when the function is used. Usage:
  290. >>> from nltk.internals import deprecated
  291. >>> @deprecated('Use foo() instead')
  292. ... def bar(x):
  293. ... print x/10
  294. """
  295. def decorator(func):
  296. msg = ("Function %s() has been deprecated. %s"
  297. % (func.__name__, message))
  298. msg = '\n' + textwrap.fill(msg, initial_indent=' ',
  299. subsequent_indent=' ')
  300. def newFunc(*args, **kwargs):
  301. warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
  302. return func(*args, **kwargs)
  303. # Copy the old function's name, docstring, & dict
  304. newFunc.__dict__.update(func.__dict__)
  305. newFunc.__name__ = func.__name__
  306. newFunc.__doc__ = func.__doc__
  307. newFunc.__deprecated__ = True
  308. # Add a @deprecated field to the docstring.
  309. _add_epytext_field(newFunc, 'deprecated', message)
  310. return newFunc
  311. return decorator
  312. class Deprecated(object):
  313. """
  314. A base class used to mark deprecated classes. A typical usage is to
  315. alert users that the name of a class has changed:
  316. >>> from nltk.internals import Deprecated
  317. >>> class NewClassName(object):
  318. ... pass # All logic goes here.
  319. ...
  320. >>> class OldClassName(Deprecated, NewClassName):
  321. ... "Use NewClassName instead."
  322. The docstring of the deprecated class will be used in the
  323. deprecation warning message.
  324. """
  325. def __new__(cls, *args, **kwargs):
  326. # Figure out which class is the deprecated one.
  327. dep_cls = None
  328. for base in _mro(cls):
  329. if Deprecated in base.__bases__:
  330. dep_cls = base; break
  331. assert dep_cls, 'Unable to determine which base is deprecated.'
  332. # Construct an appropriate warning.
  333. doc = dep_cls.__doc__ or ''.strip()
  334. # If there's a @deprecated field, strip off the field marker.
  335. doc = re.sub(r'\A\s*@deprecated:', r'', doc)
  336. # Strip off any indentation.
  337. doc = re.sub(r'(?m)^\s*', '', doc)
  338. # Construct a 'name' string.
  339. name = 'Class %s' % dep_cls.__name__
  340. if cls != dep_cls:
  341. name += ' (base class for %s)' % cls.__name__
  342. # Put it all together.
  343. msg = '%s has been deprecated. %s' % (name, doc)
  344. # Wrap it.
  345. msg = '\n' + textwrap.fill(msg, initial_indent=' ',
  346. subsequent_indent=' ')
  347. warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
  348. # Do the actual work of __new__.
  349. return object.__new__(cls, *args, **kwargs)
  350. ##########################################################################
  351. # COUNTER, FOR UNIQUE NAMING
  352. ##########################################################################
  353. class Counter:
  354. """
  355. A counter that auto-increments each time its value is read.
  356. """
  357. def __init__(self, initial_value=0):
  358. self._value = initial_value
  359. def get(self):
  360. self._value += 1
  361. return self._value
  362. ##########################################################################
  363. # Search for files/binaries
  364. ##########################################################################
  365. def find_file(filename, env_vars=(), searchpath=(),
  366. file_names=None, url=None, verbose=True):
  367. """
  368. Search for a file to be used by nltk.
  369. :param filename: The name or path of the file.
  370. :param env_vars: A list of environment variable names to check.
  371. :param file_names: A list of alternative file names to check.
  372. :param searchpath: List of directories to search.
  373. :param url: URL presented to user for download help.
  374. :param verbose: Whether or not to print path when a file is found.
  375. """
  376. if file_names is None: file_names = [filename]
  377. assert isinstance(filename, basestring)
  378. assert not isinstance(file_names, basestring)
  379. assert not isinstance(searchpath, basestring)
  380. if isinstance(env_vars, basestring):
  381. env_vars = env_vars.split()
  382. # File exists, no magic
  383. if os.path.isfile(filename):
  384. if verbose: print '[Found %s: %s]' % (filename, filename)
  385. return filename
  386. for alternative in file_names:
  387. path_to_file = os.path.join(filename, alternative)
  388. if os.path.isfile(path_to_file):
  389. if verbose: print '[Found %s: %s]' % (filename, path_to_file)
  390. return path_to_file
  391. path_to_file = os.path.join(filename, 'file', alternative)
  392. if os.path.isfile(path_to_file):
  393. if verbose: print '[Found %s: %s]' % (filename, path_to_file)
  394. return path_to_file
  395. # Check environment variables
  396. for env_var in env_vars:
  397. if env_var in os.environ:
  398. path_to_file = os.environ[env_var]
  399. if os.path.isfile(path_to_file):
  400. if verbose: print '[Found %s: %s]' % (filename, path_to_file)
  401. return path_to_file
  402. else:
  403. for alternative in file_names:
  404. path_to_file = os.path.join(os.environ[env_var],
  405. alternative)
  406. if os.path.isfile(path_to_file):
  407. if verbose: print '[Found %s: %s]'%(filename, path_to_file)
  408. return path_to_file
  409. path_to_file = os.path.join(os.environ[env_var], 'file',
  410. alternative)
  411. if os.path.isfile(path_to_file):
  412. if verbose: print '[Found %s: %s]'%(filename, path_to_file)
  413. return path_to_file
  414. # Check the path list.
  415. for directory in searchpath:
  416. for alternative in file_names:
  417. path_to_file = os.path.join(directory, alternative)
  418. if os.path.isfile(path_to_file):
  419. return path_to_file
  420. # If we're on a POSIX system, then try using the 'which' command
  421. # to find the file.
  422. if os.name == 'posix':
  423. for alternative in file_names:
  424. try:
  425. p = subprocess.Popen(['which', alternative], stdout=subprocess.PIPE)
  426. stdout, stderr = p.communicate()
  427. path = stdout.strip()
  428. if path.endswith(alternative) and os.path.exists(path):
  429. if verbose: print '[Found %s: %s]' % (filename, path)
  430. return path
  431. except KeyboardInterrupt, SystemExit:
  432. raise
  433. except:
  434. pass
  435. msg = ("NLTK was unable to find the %s file!" "\nUse software specific "
  436. "configuration paramaters" % filename)
  437. if env_vars: msg += ' or set the %s environment variable' % env_vars[0]
  438. msg += '.'
  439. if searchpath:
  440. msg += '\n\n Searched in:'
  441. msg += ''.join('\n - %s' % d for d in searchpath)
  442. if url: msg += ('\n\n For more information, on %s, see:\n <%s>' %
  443. (filename, url))
  444. div = '='*75
  445. raise LookupError('\n\n%s\n%s\n%s' % (div, msg, div))
  446. def find_binary(name, path_to_bin=None, env_vars=(), searchpath=(),
  447. binary_names=None, url=None, verbose=True):
  448. """
  449. Search for a file to be used by nltk.
  450. :param name: The name or path of the file.
  451. :param path_to_bin: The user-supplied binary location (deprecated)
  452. :param env_vars: A list of environment variable names to check.
  453. :param file_names: A list of alternative file names to check.
  454. :param searchpath: List of directories to search.
  455. :param url: URL presented to user for download help.
  456. :param verbose: Whether or not to print path when a file is found.
  457. """
  458. return find_file(path_to_bin or name, env_vars, searchpath, binary_names,
  459. url, verbose)
  460. ##########################################################################
  461. # Find Java JAR files
  462. # TODO: Add support for jar names specified as regular expressions
  463. ##########################################################################
  464. def find_jar(name, path_to_jar=None, env_vars=(),
  465. searchpath=(), url=None, verbose=True):
  466. """
  467. Search for a jar that is used by nltk.
  468. :param name: The name of the jar file
  469. :param path_to_jar: The user-supplied jar location, or None.
  470. :param env_vars: A list of environment variable names to check
  471. in addition to the CLASSPATH variable which is
  472. checked by default.
  473. :param searchpath: List of directories to search.
  474. """
  475. assert isinstance(name, basestring)
  476. assert not isinstance(searchpath, basestring)
  477. if isinstance(env_vars, basestring):
  478. env_vars = env_vars.split()
  479. # Make sure we check the CLASSPATH first
  480. env_vars = ['CLASSPATH'] + list(env_vars)
  481. # If an explicit location was given, then check it, and return it if
  482. # it's present; otherwise, complain.
  483. if path_to_jar is not None:
  484. if os.path.isfile(path_to_jar):
  485. return path_to_jar
  486. raise ValueError('Could not find %s jar file at %s' %
  487. (name, path_to_jar))
  488. # Check environment variables
  489. for env_var in env_vars:
  490. if env_var in os.environ:
  491. if env_var == 'CLASSPATH':
  492. classpath = os.environ['CLASSPATH']
  493. for cp in classpath.split(os.path.pathsep):
  494. if os.path.isfile(cp) and os.path.basename(cp) == name:
  495. if verbose: print '[Found %s: %s]' % (name, cp)
  496. return cp
  497. else:
  498. path_to_jar = os.environ[env_var]
  499. if os.path.isfile(path_to_jar) and os.path.basename(path_to_jar) == name:
  500. if verbose: print '[Found %s: %s]' % (name, path_to_jar)
  501. return path_to_jar
  502. # Check the path list.
  503. for directory in searchpath:
  504. path_to_jar = os.path.join(directory, name)
  505. if os.path.isfile(path_to_jar):
  506. if verbose: print '[Found %s: %s]' % (name, path_to_jar)
  507. return path_to_jar
  508. # If nothing was found, raise an error
  509. msg = ("NLTK was unable to find %s!" % name)
  510. if env_vars: msg += ' Set the %s environment variable' % env_vars[0]
  511. msg = textwrap.fill(msg+'.', initial_indent=' ',
  512. subsequent_indent=' ')
  513. if searchpath:
  514. msg += '\n\n Searched in:'
  515. msg += ''.join('\n - %s' % d for d in searchpath)
  516. if url: msg += ('\n\n For more information, on %s, see:\n <%s>' %
  517. (name, url))
  518. div = '='*75
  519. raise LookupError('\n\n%s\n%s\n%s' % (div, msg, div))
  520. ##########################################################################
  521. # Import Stdlib Module
  522. ##########################################################################
  523. def import_from_stdlib(module):
  524. """
  525. When python is run from within the nltk/ directory tree, the
  526. current directory is included at the beginning of the search path.
  527. Unfortunately, that means that modules within nltk can sometimes
  528. shadow standard library modules. As an example, the stdlib
  529. 'inspect' module will attempt to import the stdlib 'tokenzie'
  530. module, but will instead end up importing NLTK's 'tokenize' module
  531. instead (causing the import to fail).
  532. """
  533. old_path = sys.path
  534. sys.path = [d for d in sys.path if d not in ('', '.')]
  535. m = __import__(module)
  536. sys.path = old_path
  537. return m
  538. ##########################################################################
  539. # Abstract declaration
  540. ##########################################################################
  541. def abstract(func):
  542. """
  543. A decorator used to mark methods as abstract. I.e., methods that
  544. are marked by this decorator must be overridden by subclasses. If
  545. an abstract method is called (either in the base class or in a
  546. subclass that does not override the base class method), it will
  547. raise ``NotImplementedError``.
  548. """
  549. # Avoid problems caused by nltk.tokenize shadowing the stdlib tokenize:
  550. inspect = import_from_stdlib('inspect')
  551. # Read the function's signature.
  552. args, varargs, varkw, defaults = inspect.getargspec(func)
  553. # Create a new function with the same signature (minus defaults)
  554. # that raises NotImplementedError.
  555. msg = '%s is an abstract method.' % func.__name__
  556. signature = inspect.formatargspec(args, varargs, varkw, ())
  557. exec ('def newfunc%s: raise NotImplementedError(%r)' % (signature, msg))
  558. # Substitute in the defaults after-the-fact, since eval(repr(val))
  559. # may not work for some default values.
  560. newfunc.func_defaults = func.func_defaults
  561. # Copy the name and docstring
  562. newfunc.__name__ = func.__name__
  563. newfunc.__doc__ = func.__doc__
  564. newfunc.__abstract__ = True
  565. _add_epytext_field(newfunc, "note", "This method is abstract.")
  566. # Return the function.
  567. return newfunc
  568. ##########################################################################
  569. # Wrapper for ElementTree Elements
  570. ##########################################################################
  571. class ElementWrapper(object):
  572. """
  573. A wrapper around ElementTree Element objects whose main purpose is
  574. to provide nicer __repr__ and __str__ methods. In addition, any
  575. of the wrapped Element's methods that return other Element objects
  576. are overridden to wrap those values before returning them.
  577. This makes Elements more convenient to work with in
  578. interactive sessions and doctests, at the expense of some
  579. efficiency.
  580. """
  581. # Prevent double-wrapping:
  582. def __new__(cls, etree):
  583. """
  584. Create and return a wrapper around a given Element object.
  585. If ``etree`` is an ``ElementWrapper``, then ``etree`` is
  586. returned as-is.
  587. """
  588. if isinstance(etree, ElementWrapper):
  589. return etree
  590. else:
  591. return object.__new__(ElementWrapper, etree)
  592. def __init__(self, etree):
  593. """
  594. Initialize a new Element wrapper for ``etree``. If
  595. ``etree`` is a string, then it will be converted to an
  596. Element object using ``ElementTree.fromstring()`` first.
  597. """
  598. if isinstance(etree, basestring):
  599. etree = ElementTree.fromstring(etree)
  600. self.__dict__['_etree'] = etree
  601. def unwrap(self):
  602. """
  603. Return the Element object wrapped by this wrapper.
  604. """
  605. return self._etree
  606. ##////////////////////////////////////////////////////////////
  607. #{ String Representation
  608. ##////////////////////////////////////////////////////////////
  609. def __repr__(self):
  610. s = ElementTree.tostring(self._etree)
  611. if len(s) > 60:
  612. e = s.rfind('<')
  613. if (len(s)-e) > 30: e = -20
  614. s = '%s...%s' % (s[:30], s[e:])
  615. return '<Element %r>' % s
  616. def __str__(self):
  617. """
  618. :return: the result of applying ``ElementTree.tostring()`` to
  619. the wrapped Element object.
  620. """
  621. return ElementTree.tostring(self._etree).rstrip()
  622. ##////////////////////////////////////////////////////////////
  623. #{ Element interface Delegation (pass-through)
  624. ##////////////////////////////////////////////////////////////
  625. def __getattr__(self, attrib):
  626. return getattr(self._etree, attrib)
  627. def __setattr__(self, attr, value):
  628. return setattr(self._etree, attr, value)
  629. def __delattr__(self, attr):
  630. return delattr(self._etree, attr)
  631. def __setitem__(self, index, element):
  632. self._etree[index] = element
  633. def __delitem__(self, index):
  634. del self._etree[index]
  635. def __setslice__(self, start, stop, elements):
  636. self._etree[start:stop] = elements
  637. def __delslice__(self, start, stop):
  638. del self._etree[start:stop]
  639. def __len__(self):
  640. return len(self._etree)
  641. ##////////////////////////////////////////////////////////////
  642. #{ Element interface Delegation (wrap result)
  643. ##////////////////////////////////////////////////////////////
  644. def __getitem__(self, index):
  645. return ElementWrapper(self._etree[index])
  646. def __getslice__(self, start, stop):
  647. return [ElementWrapper(elt) for elt in self._etree[start:stop]]
  648. def getchildren(self):
  649. return [ElementWrapper(elt) for elt in self._etree]
  650. def getiterator(self, tag=None):
  651. return (ElementWrapper(elt)
  652. for elt in self._etree.getiterator(tag))
  653. def makeelement(self, tag, attrib):
  654. return ElementWrapper(self._etree.makeelement(tag, attrib))
  655. def find(self, path):
  656. elt = self._etree.find(path)
  657. if elt is None: return elt
  658. else: return ElementWrapper(elt)
  659. def findall(self, path):
  660. return [ElementWrapper(elt) for elt in self._etree.findall(path)]
  661. ######################################################################
  662. # Helper for Handling Slicing
  663. ######################################################################
  664. def slice_bounds(sequence, slice_obj, allow_step=False):
  665. """
  666. Given a slice, return the corresponding (start, stop) bounds,
  667. taking into account None indices and negative indices. The
  668. following guarantees are made for the returned start and stop values:
  669. - 0 <= start <= len(sequence)
  670. - 0 <= stop <= len(sequence)
  671. - start <= stop
  672. :raise ValueError: If ``slice_obj.step`` is not None.
  673. :param allow_step: If true, then the slice object may have a
  674. non-None step. If it does, then return a tuple
  675. (start, stop, step).
  676. """
  677. start, stop = (slice_obj.start, slice_obj.stop)
  678. # If allow_step is true, then include the step in our return
  679. # value tuple.
  680. if allow_step:
  681. step = slice_obj.step
  682. if step is None: step = 1
  683. # Use a recursive call without allow_step to find the slice
  684. # bounds. If step is negative, then the roles of start and
  685. # stop (in terms of default values, etc), are swapped.
  686. if step < 0:
  687. start, stop = slice_bounds(sequence, slice(stop, start))
  688. else:
  689. start, stop = slice_bounds(sequence, slice(start, stop))
  690. return start, stop, step
  691. # Otherwise, make sure that no non-default step value is used.
  692. elif slice_obj.step not in (None, 1):
  693. raise ValueError('slices with steps are not supported by %s' %
  694. sequence.__class__.__name__)
  695. # Supply default offsets.
  696. if start is None: start = 0
  697. if stop is None: stop = len(sequence)
  698. # Handle negative indices.
  699. if start < 0: start = max(0, len(sequence)+start)
  700. if stop < 0: stop = max(0, len(sequence)+stop)
  701. # Make sure stop doesn't go past the end of the list. Note that
  702. # we avoid calculating len(sequence) if possible, because for lazy
  703. # sequences, calculating the length of a sequence can be expensive.
  704. if stop > 0:
  705. try: sequence[stop-1]
  706. except IndexError: stop = len(sequence)
  707. # Make sure start isn't past stop.
  708. start = min(start, stop)
  709. # That's all folks!
  710. return start, stop
  711. ######################################################################
  712. # Permission Checking
  713. ######################################################################
  714. def is_writable(path):
  715. # Ensure that it exists.
  716. if not os.path.exists(path):
  717. return False
  718. # If we're on a posix system, check its permissions.
  719. if hasattr(os, 'getuid'):
  720. statdata = os.stat(path)
  721. perm = stat.S_IMODE(statdata.st_mode)
  722. # is it world-writable?
  723. if (perm & 0002):
  724. return True
  725. # do we own it?
  726. elif statdata.st_uid == os.getuid() and (perm & 0200):
  727. return True
  728. # are we in a group that can write to it?
  729. elif statdata.st_gid == os.getgid() and (perm & 0020):
  730. return True
  731. # otherwise, we can't write to it.
  732. else:
  733. return False
  734. # Otherwise, we'll assume it's writable.
  735. # [xx] should we do other checks on other platforms?
  736. return True