#modindex_common_prefix = []
# Classes to ignore for coverage reporting
-coverage_ignore_classes = ['ProductionCopy', 'WorkingCopy']
+coverage_ignore_classes = ['ProductionCopy', 'WorkingCopy', 'Dialog', 'Prompt', 'FailPrompt']
# -- Options for HTML output ---------------------------------------------------
module/wizard.app.php
module/wizard.cache
module/wizard.deploy
+ module/wizard.git
module/wizard.install
module/wizard.merge
+ module/wizard.prompt
+ module/wizard.resolve
+ module/wizard.scripts
module/wizard.shell
- module/wizard.util
+ module/wizard.sql
module/wizard.sset
+ module/wizard.tests
+ module/wizard.util
Indices and tables
------------------
--- /dev/null
+:mod:`wizard.git`
+=================
+
+.. automodule:: wizard.git
+
+Functions
+---------
+.. autofunction:: describe
+.. autofunction:: commit_configure
--- /dev/null
+:mod:`wizard.prompt`
+====================
+
+.. automodule:: wizard.prompt
+
+Functions
+---------
+.. autofunction:: make
+
+Interfaces
+----------
+.. autoclass:: PromptInterface
+ :members:
+
+Classes
+-------
+.. autoclass:: Dialog
+ :show-inheritance:
+.. autoclass:: Prompt
+ :show-inheritance:
+.. autoclass:: FailPrompt
+ :show-inheritance:
+
+Utility functions
+-----------------
+.. autofunction:: fill
+.. autofunction:: guess_dimensions
+.. autofunction:: join_or
+.. autofunction:: dialog_wrap
+.. autofunction:: prompt_wrap
+
+Exceptions
+----------
+.. autoexception:: Error
+.. autoexception:: MissingRequiredParam
+ :show-inheritance:
+.. autoexception:: UserCancel
+ :show-inheritance:
+.. autoexception:: DialogError
+ :show-inheritance:
+.. autoexception:: UnsupportedTerminal
+ :show-inheritance:
--- /dev/null
+:mod:`wizard.resolve`
+=====================
+
+.. automodule:: wizard.resolve
+
+Functions
+---------
+.. autofunction:: resolve
+.. autofunction:: is_conflict
+
+Utility functions
+-----------------
+.. autofunction:: spec_to_regex
+.. autofunction:: result_to_repl
--- /dev/null
+:mod:`wizard.scripts`
+=====================
+
+.. automodule:: wizard.scripts
+
+Functions
+---------
+.. autofunction:: fill_url
+.. autofunction:: get_quota_usage_and_limit
+.. autofunction:: get_disk_usage
+
+Exceptions
+----------
+.. autoexception:: QuotaParseError
+
--- /dev/null
+:mod:`wizard.sql`
+=================
+
+.. automodule:: wizard.sql
+
+Functions
+---------
+.. autofunction:: connect
+.. autofunction:: fill_url
+
--- /dev/null
+:mod:`wizard.tests`
+===================
+
+.. automodule:: wizard.tests
+
+Functions
+---------
+.. autofunction:: getTestFile
+
return self.detectVersionFromFile("includes/DefaultSettings.php", php.re_var("wgVersion"))
def checkWeb(self, deployment):
return self.checkWebPage(deployment, "/index.php?title=Main_Page", "<!-- Served")
- def prepareMerge(self, deployment):
- resolve.fix_newlines("LocalSettings.php")
def install(self, version, options):
util.soft_unlink("LocalSettings.php")
os.chmod("config", 0777) # XXX: vaguely sketchy
return self.checkWebPage(deployment, "", "<html")
def detectVersion(self, deployment):
return self.detectVersionFromFile("wp-includes/version.php", php.re_var("wp_version"))
- def prepareMerge(self, deployment):
- # This file shouldn't really be edited by users, but be careful: it's
- # stored as DOS and not as UNIX, so you'll get conflicts if you add this line:
- # resolve.fix_newlines("wp-config.php")
- pass
def install(self, version, options):
util.soft_unlink("wp-config.php")
return shell.safeCall("git", "describe", "--tags", strip=True)
def commit_configure():
+ """
+ Performs a commit of changes performed during configuration of an install
+ with an appropriate logfile message.
+ """
message = "Autoinstall configuration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
util.set_git_env()
try:
"""
-Interface compatible with :class:`dialog.Dialog` for doing
+Interface compatible with :class:`PromptInterface` for doing
non-ncurses interaction.
By convention, the last line of a text parameter should be
a short value with a trailing colon so that we can prompt a user
for a value immediately after it.
+
+.. testsetup:: *
+
+ from wizard.prompt import *
"""
import sys
has_dialog = False
def fill(text, width=60, **kwargs):
+ """
+ Convenience wrapper for :func:`textwrap.fill` that preserves
+ paragraphs.
+ """
return "\n\n".join(textwrap.fill(p, width=width, **kwargs) for p in text.split("\n\n"))
def guess_dimensions(text, width=60):
+ """
+ Guesses the dimensions that any given piece of text will
+ need to display on terminal, given some width.
+ """
# +1 for the fact that there's no trailing newline from fill
# +2 for the borders
# +1 as a buffer in case we underestimate
return width, fill(text, width-2).count("\n") + 1 + 2 + 1
def make(prompt, non_interactive):
+ """
+ Makes a :class:`dialog.Dialog` compatible class based on
+ configuration.
+ """
if non_interactive:
return FailPrompt()
if prompt or os.getenv('TERM') == 'dumb' or not has_dialog:
return Prompt()
def join_or(items):
+ """
+ Joins a list of disjunctions into a human readable sentence.
+
+ >>> join_or(['foo'])
+ 'foo'
+ >>> join_or(['foo', 'bar', 'baz'])
+ 'foo, bar or baz'
+ """
if len(items) == 0:
raise ValueError
elif len(items) == 1:
return items[0]
return ', '.join(items[:-1]) + ' or ' + items[-1]
+class PromptInterface(object):
+ def inputbox(self, text, init='', **kwargs):
+ """
+ Request a free-form, single line of text from the user.
+ Prompt the user using ``text``; and ``init`` is the
+ initial value filling the field; not all implementations
+ support editing ``init``. Returns the typed string.
+ """
+ raise NotImplementedError
+ def menu(self, text, choices=[], **kwargs):
+ """
+ Request a selection from a number of choices from the user.
+ Prompt the user using ``text``; ``choices`` is a list
+ of tuples of form ``(value to return, description)``, where
+ ``value to return`` is the value that this function will
+ return.
+ """
+ raise NotImplementedError
+ def passwordbox(self, text, **kwargs):
+ """
+ Securely requests a password from the user. Prompts the user
+ using ``text``; return value is the password.
+ """
+ raise NotImplementedError
+ def msgbox(self, text, **kwargs):
+ """
+ Gives the user a message that they must dismiss before proceeding.
+ """
+ raise NotImplementedError
+ def infobox(self, text, **kwargs):
+ """
+ Gives the user a non-blocking message; useful if you are about
+ to do an operation that will take some time.
+ """
+ raise NotImplementedError
+
@decorator.decorator
def dialog_wrap(f, self, text, *args, **kwargs):
+ """
+ Convenience decorator that automatically:
+
+ 1. Removes already handled keyword arguments,
+ 2. Configures the dimensions of the dialog box, and
+ 3. Handles the different ext possibilities of dialog.
+ """
if 'cmdopt' in kwargs: del kwargs['cmdopt']
if 'width' not in kwargs and 'height' not in kwargs:
kwargs["width"], kwargs["height"] = guess_dimensions(text)
else:
exit, value = result
if exit == self.dialog.DIALOG_CANCEL or exit == self.dialog.DIALOG_ESC:
- raise UserCancel
+ raise UserCancl
elif exit != self.dialog.DIALOG_OK:
# XXX: We don't support stuff like DIALOG_EXTRA or DIALOG_HELP
raise DialogError(exit)
return value
-class Dialog(object):
+class Dialog(PromptInterface):
"""Ncurses interface using dialog."""
interactive = True
def __init__(self):
@decorator.decorator
def prompt_wrap(f, self, *args, **kwargs):
+ """Convenience decorator that handles end-of-document and interrupts."""
try:
return f(self, *args, **kwargs)
except (EOFError, KeyboardInterrupt):
raise UserCancel
-class Prompt(object):
+class Prompt(PromptInterface):
+ """Simple stdin/stdout prompt object."""
interactive = True
@prompt_wrap
def inputbox(self, text, init='', **kwargs):
print ""
print fill(text.strip())
-class FailPrompt(object):
+class FailPrompt(PromptInterface):
"""
Prompt that doesn't actually ask the user; just fails with
an error message.
print fill(text.strip(), break_long_words=False)
class Error(wizard.Error):
+ """Base error class."""
pass
class MissingRequiredParam(Error):
"""Non-interactive, but we needed more info."""
def __init__(self, cmdopt):
+ """``cmdopt`` is the command line option that should be specified."""
self.cmdopt = cmdopt
def __str__(self):
return """
class DialogError(Error):
"""Dialog returned a mysterious error."""
def __init__(self, exit):
+ """``exit`` is the mysterious exit code."""
self.exitcode = exit
def __str__(self):
return """
The conflict resolution DSL is described here:
Resolutions are specified as input-output pairs. An input
-is a string with the conflict resolution markers ("<" * 7,
-"=" * 7 and ">" * 7), with the HEAD content above the equals
+is a string with the conflict resolution markers ``("<" * 7,
+"=" * 7 and ">" * 7)``, with the HEAD content above the equals
divider, and the upstream content below the equals divider.
-Lines can also be marked as "***N***" where N is a natural
+Lines can also be marked as ``***N***`` where N is a natural
number greater than 0 (i.e. 1 or more), which means that
an arbitrary number of lines may be matched and available for output.
return ("<<<<<<<[^\n]*\\\n" + ours_regex + "\|\|\|\|\|\|\|\\\n" + common_regex + "=======\\\n" + theirs_regex + ">>>>>>>[^\n]*(\\\n|$)", ours_mappings)
def result_to_repl(result, mappings):
+ """
+ Converts a list of replacement strings and or references
+ into a replacement string appropriate for a regular expression.
+ """
def ritem_to_string(r):
if type(r) is int:
return "\\%d" % mappings[r]
return "".join(map(ritem_to_string, result))
def resolve(contents, spec, result):
+ """
+ Given a conflicted file, whose contents are ``contents``, attempt
+ to resolve all conflicts that match ``spec`` with ``result``.
+ """
rstring, mappings = spec_to_regex(spec)
regex = re.compile(rstring, re.DOTALL)
repl = result_to_repl(result, mappings)
return ret
def is_conflict(contents):
+ """
+ Given ``contents``, return ``True`` if there are any conflicts in it.
+ """
# Really really simple heuristic
return "<<<<<<<" in contents
-def fix_newlines(file, log=True):
- """
- Normalizes newlines in a file into UNIX file endings. If
- ``log`` is ``True`` an info log mesage is printed if
- any normalization occurs. Return value is ``True`` if
- normalization occurred.
- """
- old_contents = open(file, "r").read()
- contents = old_contents
- while "\r\n" in contents:
- contents = contents.replace("\r\n", "\n")
- contents = contents.replace("\r", "\n")
- if contents != old_contents:
- logging.info("Converted %s to UNIX file endings" % file)
- open(file, "w").write(contents)
- return True
- return False
+"""
+This is ostensibly the place where Scripts specific code should live.
+"""
+
import os
import shlex
import errno
from wizard import shell, util
def fill_url(dir, url=None):
+ """
+ Attempts to determine the URL a directory would be web-accessible at.
+ If ``url`` is specified, automatically use it.
+ """
if url:
return url
else:
raise
return sum_sizes
+
class QuotaParseError(wizard.Error):
+ """Could not parse quota information."""
def __init__(self, msg):
self.msg = msg
def __str__(self):
import os.path
def getTestFile(file):
+ """
+ Returns the path to a testfile in this test directory.
+ """
return os.path.join(os.path.dirname(os.path.abspath(__file__)), file)