<commit_before>import tkinter as tk
from PIL import Image, ImageTk
from .pages import PageCollection
from .guitools import SelectionBox
from .io import replace_filename_index
from tkinter import filedialog, messagebox
import os
from os import path

class Application(tk.Frame):

    def __init__(self, images, master=None, save_dir=None):

        tk.Frame.__init__(self, master=master)
        self.images = PageCollection(images)

        self.pack()
        self._createWidgets()
        if save_dir:
            self.img_dirname.set(save_dir)
        self.selectbox = SelectionBox(self.canvas)

        canv_height = self.canvas.winfo_height()
        self.images.set_scale(canv_height)
        self.show_img()

    def _createWidgets(self, width=600, height=700):
        """Setup method.  Creates all buttons, canvases, and defaults before starting app."""

        self.btn_prev = tk.Button(self, text='Prev', command=self.prev_page)
        self.btn_prev.pack(side='top')

        self.btn_next = tk.Button(self, text='Next', command=self.next_page)
        self.btn_next.pack(side='top')

        self.img_dirname_label = tk.Label(self, text='Save Dir:')
        self.img_dirname_label.pack(side='top')

        self.img_dirname = tk.StringVar()
        self.img_dirname.set(os.getcwd())
        self.img_dirname_entry = tk.Entry(self, textvariable=self.img_dirname)
        self.img_dirname_entry.pack(side='top')

        self.img_dirname_btn = tk.Button(self, text='...', command=self.display_path_dialog)
        self.img_dirname_btn.pack(side='top')

        self.img_basename_label = tk.Label(self, text='Image Filename:')
        self.img_basename_label.pack(side='top')

        self.img_filename = tk.StringVar()
        self.img_filename.set("img01.jpg")
        self.img_basename = tk.Entry(self, textvariable=self.img_filename)
        self.img_basename.pack(side='top')

        self.save_btn = tk.Button(self, text="Save Image", command=self.get_subimage,
                                  state=tk.DISABLED)
        self.save_btn.pack(side='top')

        # Make the main Canvas, where most everything is drawn
        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.pack(side='right')
        self.canvas.update()

        # Set up selection rectangle functionality
        self.canvas.bind("<Button-1>", self.selectbox_create)
        self.canvas.bind("<B1-Motion>", self.selectbox_update)
        self.canvas.bind("<Button-3>", self.get_subimage)
        self.canvas.bind("<Configure>", self.on_resize)

    def display_path_dialog(self):
        dir_name = filedialog.askdirectory(title="Select a Save Directory")
        self.img_dirname.set(dir_name)

    def on_resize(self, event):
        self.images.set_scale(event.height)
        self.show_img()

    def selectbox_delete(self):
        if self.selectbox:
            self.canvas.delete(self.selectbox)
        self.selectbox = 0

    def selectbox_create(self, event):
        self.selectbox.new(event.x, event.y)
        self.save_btn.config(state=tk.DISABLED)

    def selectbox_update(self, event):
        self.selectbox.update(event.x, event.y)
        self.save_btn.config(state=tk.NORMAL)

    def show_img(self):
        """Displays a rescaled page to fit the canvas size."""
        self.selectbox.delete()
        img = self.images.get_scaled_img()

        #tkinter gotcha--must save photoimage as attribute, or it garbage collects it.
        self._photoimg = ImageTk.PhotoImage(image=img)

        self.canvas.create_image(0, 0, image=self._photoimg, anchor='nw')

    def get_subimage(self, event=None):
        coords = self.selectbox.coords
        if not coords:
            messagebox.showerror(parent=self, title="Image Not Selected",
                                 message="Please first click and drag image you wish saved.")
            return

        img = self.images.get_subimage(*coords)
        save_filename = path.join(self.img_dirname.get(), self.img_basename.get())

        # Check if path exists
        if not path.exists(path.dirname(save_filename)):
            messagebox.showerror(parent=self, title="Directory Not Found",
                                 message="Save Directory Not Found.  Please Select an Existing Directory to Continue.")
            self.display_path_dialog()
            return

        # Check if save will overwrite previous file.
        if path.exists(save_filename):
            resp = messagebox.askyesno(parent=self,
                                       title="Overwrite File",
                                       message="This will overwrite file {}.  Are you sure you want to continue?".format(self.img_filename.get()))
            if not resp:
                return
        img.save(save_filename)

        # Increment Filename index
        new_save_filename = replace_filename_index(self.img_filename.get())
        self.img_filename.set(new_save_filename)

    def next_page(self):
        self.images.next_page()
        self.show_img()
        self.save_btn.config(state=tk.DISABLED)

    def prev_page(self):
        self.images.prev_page()
        self.show_img()
        self.save_btn.config(state=tk.DISABLED)

<commit_msg>changed mouse cursor over the canvas<commit_after>import tkinter as tk
from PIL import Image, ImageTk
from .pages import PageCollection
from .guitools import SelectionBox
from .io import replace_filename_index
from tkinter import filedialog, messagebox
import os
from os import path

class Application(tk.Frame):

    def __init__(self, images, master=None, save_dir=None):

        tk.Frame.__init__(self, master=master)
        self.images = PageCollection(images)

        self.pack()
        self._createWidgets()
        if save_dir:
            self.img_dirname.set(save_dir)
        self.selectbox = SelectionBox(self.canvas)

        canv_height = self.canvas.winfo_height()
        self.images.set_scale(canv_height)
        self.show_img()

    def _createWidgets(self, width=600, height=700):
        """Setup method.  Creates all buttons, canvases, and defaults before starting app."""

        self.btn_prev = tk.Button(self, text='Prev', command=self.prev_page)
        self.btn_prev.pack(side='top')

        self.btn_next = tk.Button(self, text='Next', command=self.next_page)
        self.btn_next.pack(side='top')

        self.img_dirname_label = tk.Label(self, text='Save Dir:')
        self.img_dirname_label.pack(side='top')

        self.img_dirname = tk.StringVar()
        self.img_dirname.set(os.getcwd())
        self.img_dirname_entry = tk.Entry(self, textvariable=self.img_dirname)
        self.img_dirname_entry.pack(side='top')

        self.img_dirname_btn = tk.Button(self, text='...', command=self.display_path_dialog)
        self.img_dirname_btn.pack(side='top')

        self.img_basename_label = tk.Label(self, text='Image Filename:')
        self.img_basename_label.pack(side='top')

        self.img_filename = tk.StringVar()
        self.img_filename.set("img01.jpg")
        self.img_basename = tk.Entry(self, textvariable=self.img_filename)
        self.img_basename.pack(side='top')

        self.save_btn = tk.Button(self, text="Save Image", command=self.get_subimage,
                                  state=tk.DISABLED)
        self.save_btn.pack(side='top')

        # Make the main Canvas, where most everything is drawn
        self.canvas = tk.Canvas(self, width=width, height=height,
                                cursor='cross')
        self.canvas.pack(side='right')
        self.canvas.update()

        # Set up selection rectangle functionality
        self.canvas.bind("<Button-1>", self.selectbox_create)
        self.canvas.bind("<B1-Motion>", self.selectbox_update)
        self.canvas.bind("<Button-3>", self.get_subimage)
        self.canvas.bind("<Configure>", self.on_resize)

    def display_path_dialog(self):
        dir_name = filedialog.askdirectory(title="Select a Save Directory")
        self.img_dirname.set(dir_name)

    def on_resize(self, event):
        self.images.set_scale(event.height)
        self.show_img()

    def selectbox_delete(self):
        if self.selectbox:
            self.canvas.delete(self.selectbox)
        self.selectbox = 0

    def selectbox_create(self, event):
        self.selectbox.new(event.x, event.y)
        self.save_btn.config(state=tk.DISABLED)

    def selectbox_update(self, event):
        self.selectbox.update(event.x, event.y)
        self.save_btn.config(state=tk.NORMAL)

    def show_img(self):
        """Displays a rescaled page to fit the canvas size."""
        self.selectbox.delete()
        img = self.images.get_scaled_img()

        #tkinter gotcha--must save photoimage as attribute, or it garbage collects it.
        self._photoimg = ImageTk.PhotoImage(image=img)

        self.canvas.create_image(0, 0, image=self._photoimg, anchor='nw')

    def get_subimage(self, event=None):
        coords = self.selectbox.coords
        if not coords:
            messagebox.showerror(parent=self, title="Image Not Selected",
                                 message="Please first click and drag image you wish saved.")
            return

        img = self.images.get_subimage(*coords)
        save_filename = path.join(self.img_dirname.get(), self.img_basename.get())

        # Check if path exists
        if not path.exists(path.dirname(save_filename)):
            messagebox.showerror(parent=self, title="Directory Not Found",
                                 message="Save Directory Not Found.  Please Select an Existing Directory to Continue.")
            self.display_path_dialog()
            return

        # Check if save will overwrite previous file.
        if path.exists(save_filename):
            resp = messagebox.askyesno(parent=self,
                                       title="Overwrite File",
                                       message="This will overwrite file {}.  Are you sure you want to continue?".format(self.img_filename.get()))
            if not resp:
                return
        img.save(save_filename)

        # Increment Filename index
        new_save_filename = replace_filename_index(self.img_filename.get())
        self.img_filename.set(new_save_filename)

    def next_page(self):
        self.images.next_page()
        self.show_img()
        self.save_btn.config(state=tk.DISABLED)

    def prev_page(self):
        self.images.prev_page()
        self.show_img()
        self.save_btn.config(state=tk.DISABLED)

<|endoftext|>


<commit_before>from sys import argv
from os.path import join, dirname, abspath, isdir

if len(argv) == 2 and argv[1] == 'repl':
    # don't want to use __main__ only for repl yet, maybe we want to use it for
    # something else. So just use the keyword ``repl`` for now.
    print(join(dirname(abspath(__file__)), 'api', 'replstartup.py'))
elif len(argv) > 1 and argv[1] == 'force':
    import jedi
    import sys

    if '--debug' in sys.argv:
        jedi.set_debug_function()

    path = sys.argv[2]
    if isdir(path):
        import fnmatch
        import os

        paths = []
        for root, dirnames, filenames in os.walk(path):
            for filename in fnmatch.filter(filenames, '*.py'):
                paths.append(os.path.join(root, filename))
    else:
        paths = [path]

    try:
        for path in paths:
            for error in jedi.Script(path=path)._analysis():
                print(error)
    except Exception:
        if '--pdb' in sys.argv:
            import pdb
            pdb.post_mortem()
        else:
            raise
<commit_msg>multiple files should be scannable in the main analysis API.<commit_after>from sys import argv
from os.path import join, dirname, abspath, isdir

if len(argv) == 2 and argv[1] == 'repl':
    # don't want to use __main__ only for repl yet, maybe we want to use it for
    # something else. So just use the keyword ``repl`` for now.
    print(join(dirname(abspath(__file__)), 'api', 'replstartup.py'))
elif len(argv) > 1 and argv[1] == 'force':
    import jedi
    import sys

    if '--debug' in sys.argv:
        jedi.set_debug_function()

    for path in sys.argv[2:]:
        if path.startswith('--'):
            continue
        if isdir(path):
            import fnmatch
            import os

            paths = []
            for root, dirnames, filenames in os.walk(path):
                for filename in fnmatch.filter(filenames, '*.py'):
                    paths.append(os.path.join(root, filename))
        else:
            paths = [path]

        try:
            for path in paths:
                for error in jedi.Script(path=path)._analysis():
                    print(error)
        except Exception:
            if '--pdb' in sys.argv:
                import pdb
                pdb.post_mortem()
            else:
                raise
<|endoftext|>


<commit_before>import xml.etree.ElementTree as xml
from ...Helpers import xcrun_helper
from ..PBX import PBXResolver
from ..PBX.PBX_Constants import *
from ..BuildSystem import xcbuildsystem
from .Base_Action import *
from .BuildActionEntry import *
from ...Helpers import logging_helper
import os

class BuildAction(Base_Action):

    def __init__(self, action_xml):
        self.contents = action_xml;
        if 'parallelizeBuildables' in self.contents.keys():
            self.parallel = self.contents.get('parallelizeBuildables');
        if 'buildImplicitDependencies' in self.contents.keys():
            self.implicit = self.contents.get('buildImplicitDependencies');
        self.children = list(map(lambda entry: BuildActionEntry(entry), list(self.contents.find('./BuildActionEntries'))));

    def performAction(self, container, xcparse_object, configuration_name, additional_settings):
        """
        build_system = xcbuildsystem object - create with `xcbuildsystem()`
        container = xcscheme object - scheme that is having an action performed
        xcparse_object = xcparse object
        scheme_config_settings = dictionary containing any additional environment variables to set
        """
        # iterate over the BuildActionEnties in the BuildAction
        for child in self.children:
            # based on the reference to the project container, get the project file path
            project_path = xcrun_helper.resolvePathFromLocation(child.target.ReferencedContainer, container[2].path.base_path, container[2].path.base_path);
            # getting the xcodeproj object for the child dependency
            project = filter(lambda proj: proj.path.obj_path == project_path, xcparse_object.projects());
            if len(project) > 0:
                project = project[0];
            else:
                # if it isn't part of the xcparse object for some reason, make a new project object
                project = xcparse_object.project_constructor(project_path);

            # this is a conditional for testing purposes, eventually this check and dispatch to xcodebuild will go away
            if USE_XCODE_BUILD == 1:
                # using the xcode build system
                xcrun_helper.perform_xcodebuild(project, container[1].name, 'build', additional_settings);
            else:
                # using the xcparse build system
                self.buildTarget(project, configuration_name, child.target.BlueprintIdentifier);

    def buildTarget(self, project, configuration_name, target_identifier):
        """
        This method dispatches building a target in a project file.

        project = xcodeproj object - taken from BuildAction.performAction()
        target_identifier = string identifier of the object in the xcodeproj file
        """
        # check to make sure that the target is a valid dependency
        if target_identifier in project.contents[kPBX_objects].keys():
            # get the target from the project file
            target = filter(lambda target: target.identifier == target_identifier, project.targets());
            if len(target) > 0:
                target = target[0];
            else:
                logging_helper.getLogger().error('[BuildAction]: Could not find a target with identifier "%s" in project "%s"' % (target_identifier, project.path.root_path));

            # make sure that the project was found
            if target != None:
                print target.name;
                print '========================';

                # building any explicit dependencies first
                for dependent in target.dependencies:
                    self.buildTarget(project, configuration_name, dependent.proxy.remoteGlobalIDString);

                # create a new build system environment for this target to build in
                build_system = xcbuildsystem();
                # setting up environment first, cannot rely on build phase ordering to initialize this first
                build_system.initEnvironment(project, configuration_name);
                build_system.environment.setValueForKey('ACTION', 'build', {});
                build_system.environment.setValueForKey('BUILD_COMPONENTS', build_system.environment.getBuildComponents(), {});
                build_system.environment.setValueForKey('TARGET_NAME', target.name, {}, 'target');
                build_system.environment.setValueForKey('TARGETNAME', target.name, {}, 'target');
                build_system.environment.setValueForKey('PRODUCT_NAME', target.productName, {});
                # setting the project level build environment
                project_build_settings = project.rootObject.buildConfigurationList.buildConfigurationWithName(configuration_name).buildSettings;
                for setting in project_build_settings.keys():
                    build_system.environment.setValueForKey(str(setting), project_build_settings[setting], {}, 'project');
                # setting the target level build environment
                target_build_config = target.buildConfigurationList.buildConfigurationWithName(configuration_name);
                target_build_settings = target_build_config.buildSettings;
                for setting in target_build_settings.keys():
                    build_system.environment.setValueForKey(str(setting), target_build_settings[setting], {}, 'target');
                # setting the config level build environment
                config_build_settings = target_build_config.xcconfig;
                if config_build_settings != None:
                    build_system.environment.applyConfig(config_build_settings);
                # setting the defaults
                build_system.environment.loadDefaults();

                platform_specs_path = os.path.join(build_system.environment.valueForKey('PLATFORM_DIR'), 'Developer/Library/Xcode/Specifications');
                build_system.loadSpecsAtPath(platform_specs_path);

                # getting the target type from the target
                if hasattr(target, kPBX_TARGET_productType):
                    # locating the target type spec to load the default build settings from it
                    target_spec = build_system.getSpecForIdentifier(target.productType);
                    for setting in target_spec.contents['DefaultBuildProperties'].keys():
                        build_system.environment.setValueForKey(str(setting), target_spec.contents['DefaultBuildProperties'][setting], {}, 'target');

                    # locating the package type spec to load the default build settings
                    package_spec = build_system.getSpecForIdentifier(str(target_spec.contents['PackageTypes'][0]));
                    for setting in package_spec.contents['DefaultBuildSettings'].keys():
                        build_system.environment.setValueForKey(str(setting), package_spec.contents['DefaultBuildSettings'][setting], {}, 'target');

                # running build phases for this target
                target.executeBuildPhases(build_system);
                <commit_msg>comments on loading platform specific schemes<commit_after>import xml.etree.ElementTree as xml
from ...Helpers import xcrun_helper
from ..PBX import PBXResolver
from ..PBX.PBX_Constants import *
from ..BuildSystem import xcbuildsystem
from .Base_Action import *
from .BuildActionEntry import *
from ...Helpers import logging_helper
import os

class BuildAction(Base_Action):

    def __init__(self, action_xml):
        self.contents = action_xml;
        if 'parallelizeBuildables' in self.contents.keys():
            self.parallel = self.contents.get('parallelizeBuildables');
        if 'buildImplicitDependencies' in self.contents.keys():
            self.implicit = self.contents.get('buildImplicitDependencies');
        self.children = list(map(lambda entry: BuildActionEntry(entry), list(self.contents.find('./BuildActionEntries'))));

    def performAction(self, container, xcparse_object, configuration_name, additional_settings):
        """
        build_system = xcbuildsystem object - create with `xcbuildsystem()`
        container = xcscheme object - scheme that is having an action performed
        xcparse_object = xcparse object
        scheme_config_settings = dictionary containing any additional environment variables to set
        """
        # iterate over the BuildActionEnties in the BuildAction
        for child in self.children:
            # based on the reference to the project container, get the project file path
            project_path = xcrun_helper.resolvePathFromLocation(child.target.ReferencedContainer, container[2].path.base_path, container[2].path.base_path);
            # getting the xcodeproj object for the child dependency
            project = filter(lambda proj: proj.path.obj_path == project_path, xcparse_object.projects());
            if len(project) > 0:
                project = project[0];
            else:
                # if it isn't part of the xcparse object for some reason, make a new project object
                project = xcparse_object.project_constructor(project_path);

            # this is a conditional for testing purposes, eventually this check and dispatch to xcodebuild will go away
            if USE_XCODE_BUILD == 1:
                # using the xcode build system
                xcrun_helper.perform_xcodebuild(project, container[1].name, 'build', additional_settings);
            else:
                # using the xcparse build system
                self.buildTarget(project, configuration_name, child.target.BlueprintIdentifier);

    def buildTarget(self, project, configuration_name, target_identifier):
        """
        This method dispatches building a target in a project file.

        project = xcodeproj object - taken from BuildAction.performAction()
        target_identifier = string identifier of the object in the xcodeproj file
        """
        # check to make sure that the target is a valid dependency
        if target_identifier in project.contents[kPBX_objects].keys():
            # get the target from the project file
            target = filter(lambda target: target.identifier == target_identifier, project.targets());
            if len(target) > 0:
                target = target[0];
            else:
                logging_helper.getLogger().error('[BuildAction]: Could not find a target with identifier "%s" in project "%s"' % (target_identifier, project.path.root_path));

            # make sure that the project was found
            if target != None:
                print target.name;
                print '========================';

                # building any explicit dependencies first
                for dependent in target.dependencies:
                    self.buildTarget(project, configuration_name, dependent.proxy.remoteGlobalIDString);

                # create a new build system environment for this target to build in
                build_system = xcbuildsystem();
                # setting up environment first, cannot rely on build phase ordering to initialize this first
                build_system.initEnvironment(project, configuration_name);
                build_system.environment.setValueForKey('ACTION', 'build', {});
                build_system.environment.setValueForKey('BUILD_COMPONENTS', build_system.environment.getBuildComponents(), {});
                build_system.environment.setValueForKey('TARGET_NAME', target.name, {}, 'target');
                build_system.environment.setValueForKey('TARGETNAME', target.name, {}, 'target');
                build_system.environment.setValueForKey('PRODUCT_NAME', target.productName, {});
                # setting the project level build environment
                project_build_settings = project.rootObject.buildConfigurationList.buildConfigurationWithName(configuration_name).buildSettings;
                for setting in project_build_settings.keys():
                    build_system.environment.setValueForKey(str(setting), project_build_settings[setting], {}, 'project');
                # setting the target level build environment
                target_build_config = target.buildConfigurationList.buildConfigurationWithName(configuration_name);
                target_build_settings = target_build_config.buildSettings;
                for setting in target_build_settings.keys():
                    build_system.environment.setValueForKey(str(setting), target_build_settings[setting], {}, 'target');
                # setting the config level build environment
                config_build_settings = target_build_config.xcconfig;
                if config_build_settings != None:
                    build_system.environment.applyConfig(config_build_settings);
                # setting the defaults
                build_system.environment.loadDefaults();

                # this loads the platform target and package types
                platform_specs_path = os.path.join(build_system.environment.valueForKey('PLATFORM_DIR'), 'Developer/Library/Xcode/Specifications');
                build_system.loadSpecsAtPath(platform_specs_path);

                # getting the target type from the target
                if hasattr(target, kPBX_TARGET_productType):
                    # locating the target type spec to load the default build settings from it
                    target_spec = build_system.getSpecForIdentifier(target.productType);
                    for setting in target_spec.contents['DefaultBuildProperties'].keys():
                        build_system.environment.setValueForKey(str(setting), target_spec.contents['DefaultBuildProperties'][setting], {}, 'target');

                    # locating the package type spec to load the default build settings
                    package_spec = build_system.getSpecForIdentifier(str(target_spec.contents['PackageTypes'][0]));
                    for setting in package_spec.contents['DefaultBuildSettings'].keys():
                        build_system.environment.setValueForKey(str(setting), package_spec.contents['DefaultBuildSettings'][setting], {}, 'target');

                # running build phases for this target
                target.executeBuildPhases(build_system);
                <|endoftext|>


<commit_before>from __future__ import unicode_literals
from abc import ABCMeta, abstractmethod
from six import with_metaclass
from .types import check_signatures_are_equal

import inspect

__all__ = (
    'Filter',
    'Never',
    'Always',
    'Condition',
)

class Filter(with_metaclass(ABCMeta, object)):
    """
    Filter to activate/deactivate a feature, depending on a condition.
    The return value of ``__call__`` will tell if the feature should be active.
    """
    @abstractmethod
    def __call__(self, *a, **kw):
        """
        The actual call to evaluate the filter.
        """
        return True

    def __and__(self, other):
        """
        Chaining of filters using the & operator.
        """
        if other is None or isinstance(other, Always):
            return self
        elif isinstance(other, Never):
            return other
        else:
            assert isinstance(other, Filter), 'Expecting filter, got %r' % other
            return _AndList([self, other])

    def __or__(self, other):
        """
        Chaining of filters using the | operator.
        """
        if other is None or isinstance(other, Never):
            return self
        elif isinstance(other, Always):
            return other
        else:
            assert isinstance(other, Filter), 'Expecting filter, got %r' % other
            return _OrList([self, other])

    def __invert__(self):
        """
        Inverting of filters using the ~ operator.
        """
        return _Invert(self)

    def __nonzero__(self):
        """
        By purpose, we don't allow bool(...) operations directly on a filter,
        because because the meaning is ambigue.

        Executing a filter has to be done always by calling it. Providing
        defaults for `None` values should be done through an `is None` check
        instead of for instance ``filter1 or Always()``.
        """
        raise TypeError

    def getargspec(self):
        """
        Return an Arguments object for this filter. This is used for type
        checking.
        """
        return inspect.getargspec(self.__call__)

class _AndList(Filter):
    """
    Result of &-operation between several filters.
    """
    def __init__(self, filters):
        all_filters = []

        for f in filters:
            if isinstance(f, _AndList):  # Turn nested _AndLists into one.
                all_filters.extend(f.filters)
            else:
                all_filters.append(f)

        # Make sure that all chained filters have the same signature.
        check_signatures_are_equal(all_filters)

        self.filters = all_filters

    def getargspec(self):
        return self.filters[0].getargspec()

    def __call__(self, *a, **kw):
        return all(f(*a, **kw) for f in self.filters)

    def __repr__(self):
        return '&'.join(repr(f) for f in self.filters)

class _OrList(Filter):
    """
    Result of |-operation between several filters.
    """
    def __init__(self, filters):
        all_filters = []

        for f in filters:
            if isinstance(f, _OrList):  # Turn nested _OrLists into one.
                all_filters.extend(f.filters)
            else:
                all_filters.append(f)

        # Make sure that all chained filters have the same signature.
        check_signatures_are_equal(all_filters)

        self.filters = all_filters

    def getargspec(self):
        return self.filters[0].getargspec()

    def __call__(self, *a, **kw):
        return any(f(*a, **kw) for f in self.filters)

    def __repr__(self):
        return '|'.join(repr(f) for f in self.filters)

class _Invert(Filter):
    """
    Negation of another filter.
    """
    def __init__(self, filter):
        self.filter = filter

    def __call__(self, *a, **kw):
        return not self.filter(*a, **kw)

    def __repr__(self):
        return '~%r' % self.filter

    def getargspec(self):
        return self.filter.getargspec()

class Always(Filter):
    """
    Always enable feature.
    """
    def __call__(self, *a, **kw):
        return True

    def __invert__(self):
        return Never()

class Never(Filter):
    """
    Never enable feature.
    """
    def __call__(self, *a, **kw):
        return False

    def __invert__(self):
        return Always()

class Condition(Filter):
    """
    Turn any callable (which takes a cli and returns a boolean) into a Filter.

    This can be used as a decorator::

        @Condition
        def feature_is_active(cli):  # `feature_is_active` becomes a Filter.
            return True

    :param func: Callable which takes a `CommandLineInterface` and returns a
                 boolean.
    """
    def __init__(self, func):
        assert callable(func)
        self.func = func

    def __call__(self, *a, **kw):
        return self.func(*a, **kw)

    def __repr__(self):
        return 'Condition(%r)' % self.func

    def getargspec(self):
        return inspect.getargspec(self.func)
<commit_msg>Cache AND and OR operations between filters.<commit_after>from __future__ import unicode_literals
from abc import ABCMeta, abstractmethod
from six import with_metaclass
from .types import check_signatures_are_equal

import inspect

__all__ = (
    'Filter',
    'Never',
    'Always',
    'Condition',
)

class Filter(with_metaclass(ABCMeta, object)):
    """
    Filter to activate/deactivate a feature, depending on a condition.
    The return value of ``__call__`` will tell if the feature should be active.
    """
    @abstractmethod
    def __call__(self, *a, **kw):
        """
        The actual call to evaluate the filter.
        """
        return True

    def __and__(self, other):
        """
        Chaining of filters using the & operator.
        """
        if other is None or isinstance(other, Always):
            return self
        elif isinstance(other, Never):
            return other
        else:
            assert isinstance(other, Filter), 'Expecting filter, got %r' % other
            return _and(self, other)

    def __or__(self, other):
        """
        Chaining of filters using the | operator.
        """
        if other is None or isinstance(other, Never):
            return self
        elif isinstance(other, Always):
            return other
        else:
            assert isinstance(other, Filter), 'Expecting filter, got %r' % other
            return _or(self, other)

    def __invert__(self):
        """
        Inverting of filters using the ~ operator.
        """
        return _Invert(self)

    def __nonzero__(self):
        """
        By purpose, we don't allow bool(...) operations directly on a filter,
        because because the meaning is ambigue.

        Executing a filter has to be done always by calling it. Providing
        defaults for `None` values should be done through an `is None` check
        instead of for instance ``filter1 or Always()``.
        """
        raise TypeError

    def getargspec(self):
        """
        Return an Arguments object for this filter. This is used for type
        checking.
        """
        return inspect.getargspec(self.__call__)

class _AndCache(dict):
    """
    Cache for And operation between filters.
    (Filter classes are stateless, so we can reuse them.)

    Note: This could be a memory leak if we keep creating filters at runtime.
          If that is True, the filters should be weakreffed (not the tuple of
          filters), and tuples should be removed when one of these filters is
          removed. In practise however, there is a finite amount of filters.
    """
    def __missing__(self, filters):
        result = _AndList(filters)
        self[filters] = result
        return result

class _OrCache(dict):
    """ Cache for Or operation between filters. """
    def __missing__(self, filters):
        result = _OrList(filters)
        self[filters] = result
        return result

_and_cache = _AndCache()
_or_cache = _OrCache()

def _and(filter1, filter2):
    """
    And operation between two filters.
    """
    return _and_cache[filter1, filter2]

def _or(filter1, filter2):
    """
    Or operation between two filters.
    """
    return _or_cache[filter1, filter2]

class _AndList(Filter):
    """
    Result of &-operation between several filters.
    """
    def __init__(self, filters):
        all_filters = []

        for f in filters:
            if isinstance(f, _AndList):  # Turn nested _AndLists into one.
                all_filters.extend(f.filters)
            else:
                all_filters.append(f)

        # Make sure that all chained filters have the same signature.
        check_signatures_are_equal(all_filters)

        self.filters = all_filters

    def getargspec(self):
        return self.filters[0].getargspec()

    def __call__(self, *a, **kw):
        return all(f(*a, **kw) for f in self.filters)

    def __repr__(self):
        return '&'.join(repr(f) for f in self.filters)

class _OrList(Filter):
    """
    Result of |-operation between several filters.
    """
    def __init__(self, filters):
        all_filters = []

        for f in filters:
            if isinstance(f, _OrList):  # Turn nested _OrLists into one.
                all_filters.extend(f.filters)
            else:
                all_filters.append(f)

        # Make sure that all chained filters have the same signature.
        check_signatures_are_equal(all_filters)

        self.filters = all_filters

    def getargspec(self):
        return self.filters[0].getargspec()

    def __call__(self, *a, **kw):
        return any(f(*a, **kw) for f in self.filters)

    def __repr__(self):
        return '|'.join(repr(f) for f in self.filters)

class _Invert(Filter):
    """
    Negation of another filter.
    """
    def __init__(self, filter):
        self.filter = filter

    def __call__(self, *a, **kw):
        return not self.filter(*a, **kw)

    def __repr__(self):
        return '~%r' % self.filter

    def getargspec(self):
        return self.filter.getargspec()

class Always(Filter):
    """
    Always enable feature.
    """
    def __call__(self, *a, **kw):
        return True

    def __invert__(self):
        return Never()

class Never(Filter):
    """
    Never enable feature.
    """
    def __call__(self, *a, **kw):
        return False

    def __invert__(self):
        return Always()

class Condition(Filter):
    """
    Turn any callable (which takes a cli and returns a boolean) into a Filter.

    This can be used as a decorator::

        @Condition
        def feature_is_active(cli):  # `feature_is_active` becomes a Filter.
            return True

    :param func: Callable which takes a `CommandLineInterface` and returns a
                 boolean.
    """
    def __init__(self, func):
        assert callable(func)
        self.func = func

    def __call__(self, *a, **kw):
        return self.func(*a, **kw)

    def __repr__(self):
        return 'Condition(%r)' % self.func

    def getargspec(self):
        return inspect.getargspec(self.func)
<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

"""Classes for version structures and strings"""

import copy
import time
import string

class AbstractVersion:

    """
    Ancestor class for all versions (as opposed to branches)
    """

    def __init__(self):
	pass

class NewVersion(AbstractVersion):

    """
    Class used as a marker for new (as yet undefined) versions.
    """

    def asString(self):
	return "@NEW@"

    def freeze(self):
	return "@NEW@"

    def __init__(self):
	self.timeStamp = 1

class AbstractBranch:

    """
    Ancestor class for all branches (as opposed to versions)
    """

    def __init__(self):
	pass

class VersionRelease(AbstractVersion):

    """
    Version element for a version/release pair. These are formatted as
    "version-release", with no hyphen allowed in either portion. The
    release must be a simple integer or two integers separated by a
    decimal point.
    """

    def __str__(self):
	"""
	Returns a string representation of a version/release pair.
	"""
	if self.buildCount != None:
	    return "%s-%d.%d" % (self.version, self.release, self.buildCount)

	return self.version + '-' + str(self.release)

    def getVersion(self):
	"""
	Returns the version string of a version/release pair.
	"""

	return self.version

    def equal(self, version):
	"""
	Compares two version-type objects and tells if they are the same
	or not.

	@rtype: boolean
	"""
	if (type(self) == type(version) and self.version == version.version
		and self.release == version.release
		and self.buildCount == version.buildCount):
	    return 1
	return 0

    def incrementRelease(self):
	"""
	Incremements the release number.
	"""
	self.release += 1

    def incrementBuildCount(self):
	"""
	Incremements the build count
	"""
	if self.buildCount:
	    self.buildCount += 1
	else:
	    self.buildCount = 1

    def __init__(self, value):
	"""
	Initialize a VersionRelease object from a string representation
	of a version release. ParseError exceptions are thrown if the
	string representation is ill-formed.

	@param value: String representation of a VersionRelease
	@type value: string
	"""
	if value.find("@") != -1:
	    raise ParseError, "version/release pairs may not contain @ signs"
	cut = value.find("-")
	if cut == -1:
	    raise ParseError, ("version/release pair was expected")
	self.version = value[:cut]

	try:
	    int(self.version[0])
	except:
	    raise ParseError, \
		("version numbers must be begin with a digit: %s" % value)

	fullRelease = value[cut + 1:]
	cut = fullRelease.find(".")
	if cut != -1:
	    self.release = fullRelease[:cut]
	    self.buildCount = fullRelease[cut + 1:]
	else:
	    self.release = fullRelease
	    self.buildCount = None

	try:
	    self.release = int(self.release)
	except:
	    raise ParseError, ("release numbers must be all numeric: %s" % value)
	if self.buildCount:
	    try:
		self.buildCount = int(self.buildCount)
	    except:
		raise ParseError, \
		    ("build count numbers must be all numeric: %s" % value)

class BranchName(AbstractBranch):

    """
    Stores a branch name, which is the same as a nickname. Branch names
    are of the form hostname@branch.
    """

    def __str__(self):
	"""
	Returns the string representation of a branch name.
	"""
	return self.host + '@' + str(self.branch)

    def getHost(self):
	return self.host

    def equal(self, version):
	"""
	Compares the BranchName object to another object, and returns
	true if they refer to the same branch.

	@param version: version to compare against
	@type version: instance
	@rtype: boolean
	"""
	if (self.__class__ == version.__class__
	     and self.host == version.host and self.branch == version.branch):
	    return 1
	return 0

    def __init__(self, value):
	"""
	Parses a branch name string into a BranchName object. A ParseError is
	thrown if the BranchName is not well formed.

	@param value: String representation of a BranchName
	@type value: str
	"""
	if value.find("@") == -1:
	    raise ParseError, "@ expected between hostname and branch name"
	if value.find("/") != -1:
	    raise ParseError, "/ should not appear in a branch name"

	(self.host, self.branch) = value.split("@", 1)
	if self.branch.find("@") != -1:
	    raise ParseError, ("branch names may not have @ signs: %s" % value)

class LocalBranch(BranchName):

    """
    Class defining the local branch.
    """

    def __init__(self):
	BranchName.__init__(self, "localhost@LOCAL")

class Version:

    """
    Class representing a version. Versions are a list of AbstractBranch,
    AbstractVersion sequences. If the last item is an AbstractBranch (meaning
    an odd number of objects are in the list, the version represents
    a branch. A version includes a time stamp, which is used for
    ordering.
    """

    def appendVersionRelease(self, version, release):
	"""
	Converts a branch to a version. The version/release passed in
	are converted to a VersionRelease object and appended to the
	branch this object represented. The time stamp is reset as
	a new version has been created.

	@param version: string representing a version
	@type version: str
	@param release: release number
	@type release: int
	"""
	assert(self.isBranch())
	self.versions.append(VersionRelease("%s-%d" % (version, release)))
	self.timeStamp = time.time()

    def appendVersionReleaseObject(self, verRel):
	"""
	Converts a branch to a version. The version/release passed in
	are appended to the branch this object represented. The time
	stamp is reset as a new version has been created.

	@param version: object for the version and release
	@type version: VersionRelease
	@param release: release number
	@type release: int
	"""
	assert(self.isBranch())
	self.versions.append(verRel)
	self.timeStamp = time.time()

    def incrementRelease(self):
	"""
	The release number for the final element in the version is
	incremented by one and the time stamp is reset.
	"""
	assert(self.isVersion())

	self.versions[-1].incrementRelease()
	self.timeStamp = time.time()

    def incrementBuildCount(self):
	"""
	The build count number for the final element in the version is
	incremented by one and the time stamp is reset.
	"""
	assert(self.isVersion())

	self.versions[-1].incrementBuildCount()
	self.timeStamp = time.time()

    def trailingVersion(self):
	"""
	Returns the AbstractVersion object at the end of the version.

	@rtype: AbstactVersion
	"""
	assert(self.isVersion())

	return self.versions[-1]

    def _listsEqual(self, list, other):
	if len(other.versions) != len(list): return 0

	for i in range(0, len(list)):
	    if not list[i].equal(other.versions[i]): return 0

	return 1

    def equal(self, other):
	"""
	Compares this object to another Version object to see if they
	are the same.

	@rtype: boolean
	"""
	return self._listsEqual(self.versions, other)

    def asString(self, defaultBranch = None):
	"""
	Returns a string representation of the version.

	@param defaultBranch: If set this is stripped fom the beginning
	of the version to give a shorter string representation.
	@type defaultBranch: Version
	@rtype: str
	"""
	list = self.versions
	s = "/"

	if defaultBranch and len(defaultBranch.versions) < len(self.versions):
	    start = Version(self.versions[0:len(defaultBranch.versions)], 0)
	    if start.equal(defaultBranch):
		list = self.versions[len(defaultBranch.versions):]
		s = ""

	for version in list:
	    s = s + ("%s/" % version)

	return s[:-1]

    def freeze(self, defaultBranch = None):
	"""
	Returns a complete string representation of the version, including
	the time stamp.

	@rtype: str
	"""
	return ("%.3f:" % self.timeStamp) + self.asString(defaultBranch)

    def isBranch(self):
	"""
	Tests whether or not the current object is a branch.

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], BranchName)

    def isVersion(self):
	"""
	Tests whether or not the current object is a version (not a branch).

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], VersionRelease)

    def isLocal(self):
    	"""
	Tests whether this is the local branch, or is a version on
	the local branch

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], LocalBranch) or    \
	    (len(self.versions) > 1 and
	     isinstance(self.versions[-2], LocalBranch))

    def onBranch(self, branch):
	"""
	Tests whether or not the current object is a version on the
	specified branch.

	@rtype: boolean
	"""
	if self.isBranch(): return 0
	return self._listsEqual(self.versions[:-1], branch)

    def branch(self):
	"""
	Returns the branch this version is part of.

	@rtype: Version
	"""
	assert(not self.isBranch())
	return Version(self.versions[:-1], 0)

    def branchNickname(self):
	"""
	Returns the BranchName object at the end of a branch. This is
	known as the branch nick name, as is used in VersionedFiles as
	an index.

	@rtype: BranchName
	"""
	assert(self.isBranch())
	return self.versions[-1]

    def parent(self):
	"""
	Returns the parent version for this version (the version this
	object's branch branched from.

	@rtype: Version
	"""
	assert(self.isVersion())
	assert(len(self.versions) > 3)
	return Version(self.versions[:-2], 0)

    def parentNode(self):
	"""
	Returns the parent version of a branch.

	@rtype: Version
	"""
	assert(self.isBranch())
	assert(len(self.versions) >= 3)
	return Version(self.versions[:-1], 0)

    def hasParent(self):
	"""
	Tests whether or not the current branch or version has a parent.
	True for all versions other then those on trunks.

	@rtype: boolean
	"""
	return(len(self.versions) >= 3)

    def isBefore(self, other):
	"""
	Tests whether the parameter is a version earlier then this object.

	@param other: Object to test against
	@type other: Version
	@rtype: boolean
	"""
	return self.timeStamp < other.timeStamp

    def isAfter(self, other):
	"""
	Tests whether the parameter is a version later then this object.

	@param other: Object to test against
	@type other: Version
	@rtype: boolean
	"""
	return self.timeStamp > other.timeStamp

    def copy(self):
	"""
	Returns a Version object which is a copy of this object. The
	result can be modified without affecting this object in any way.j

	@rtype: Version
	"""

        return copy.deepcopy(self)

    def fork(self, branch, sameVerRel = True):
	"""
	Creates a new branch from this version.

	@param branch: Branch to create for this version
	@type branch: AbstractBranch
	@param sameVerRel: If set, the new branch is turned into a version
	on the branch using the same version and release as the original
	verison.
	@type sameVerRel: boolean
	@rtype: Version
	"""
	assert(isinstance(branch, AbstractBranch))
	newlist = [ branch ]

	if sameVerRel:
	    newlist.append(self.versions[-1])

	return Version(self.versions + newlist, time.time())

    def parseVersionString(self, ver, defaultBranch = None):
	"""
	Converts a string representation of a version into a VersionRelease
	object.

	@param ver: version string
	@type ver: str
	@param defaultBranch: if provided and the ver parameter is not
	fully-qualified (it doesn't begin with a /), ver is taken to
	be relative to this branch.
	@type defaultBranch: Version
	"""
	if ver[0] != "/":
            # XXX broken code, no defaultBranch in this scope
	    if not defaultBranch:
		raise KeyError, "relative version given without a default " \
			        "branch"
	    ver = defaultBranch.asString() + "/" + ver

	parts = ver.split("/")
	del parts[0]	# absolute versions start with a /

	v = []
	while parts:
	    if parts[0] == "localhost@LOCAL":
		v.append(LocalBranch())
	    else:
		v.append(BranchName(parts[0]))

	    if len(parts) >= 2:
		v.append(VersionRelease(parts[1]))
		parts = parts[2:]
	    else:
		parts = None

	return v

    """
    Creates a Version object from a list of AbstractBranch and AbstractVersion
    objects.
    """
    def __init__(self, versionList, timeStamp):
	self.versions = versionList
	self.timeStamp = timeStamp

def ThawVersion(ver):
    if ver == "@NEW@":
	return NewVersion()
    return _ThawVersion(ver)

class _ThawVersion(Version):

    """
    Provides a version object from a frozen version string.
    """

    def __init__(self, fullString):
	"""
	Initializes a ThawVersion object.

	@param fullString: Frozen representation of a Version object.
	@type fullString: str
	"""
	(timeStr, ver) = fullString.split(":")

	timeVal = float(timeStr)
	v = self.parseVersionString(ver)

	Version.__init__(self, v, timeVal)

def VersionFromString(ver, defaultBranch = None):
    if ver == "@NEW@":
	return NewVersion()
    return _VersionFromString(ver, defaultBranch)

class _VersionFromString(Version):

    """
    Provides a version object from a string representation of a version.
    The time stamp is set to 0, so this object cannot be properly ordered
    with respect to other versions.
    """

    def __init__(self, ver, defaultBranch = None):
	"""
	Initializes a VersionFromString object.

	@param ver: string representation of a version
	@type ver: str
	@param defaultBranch: if provided and the ver parameter is not
	fully-qualified (it doesn't begin with a /), ver is taken to
	be relative to this branch.
	@type defaultBranch: Version
	"""
	if ver[0] == "@NEW@":
	    return NewVersion()

	if ver[0] != "/":
	    ver = defaultBranch.asString() + "/" + ver

	v = self.parseVersionString(ver, defaultBranch)

	Version.__init__(self, v, 0)

class VersionsError(Exception):

    """
    Ancestor for all exceptions raised by the versions module.
    """

    pass

class ParseError(VersionsError):

    """
    Indicates that an error occured turning a string into an object
    in the versions module.
    """

    def __repr__(self):
	return self.str

    def __str__(self):
	return self.str

    def __init__(self, str):
	self.str = str
<commit_msg>docstring fixup<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

"""Classes for version structures and strings"""

import copy
import time
import string

class AbstractVersion:

    """
    Ancestor class for all versions (as opposed to branches)
    """

    def __init__(self):
	pass

class NewVersion(AbstractVersion):

    """
    Class used as a marker for new (as yet undefined) versions.
    """

    def asString(self):
	return "@NEW@"

    def freeze(self):
	return "@NEW@"

    def __init__(self):
	self.timeStamp = 1

class AbstractBranch:

    """
    Ancestor class for all branches (as opposed to versions)
    """

    def __init__(self):
	pass

class VersionRelease(AbstractVersion):

    """
    Version element for a version/release pair. These are formatted as
    "version-release", with no hyphen allowed in either portion. The
    release must be a simple integer or two integers separated by a
    decimal point.
    """

    def __str__(self):
	"""
	Returns a string representation of a version/release pair.
	"""
	if self.buildCount != None:
	    return "%s-%d.%d" % (self.version, self.release, self.buildCount)

	return self.version + '-' + str(self.release)

    def getVersion(self):
	"""
	Returns the version string of a version/release pair.
	"""

	return self.version

    def equal(self, version):
	"""
	Compares two version-type objects and tells if they are the same
	or not.

	@rtype: boolean
	"""
	if (type(self) == type(version) and self.version == version.version
		and self.release == version.release
		and self.buildCount == version.buildCount):
	    return 1
	return 0

    def incrementRelease(self):
	"""
	Incremements the release number.
	"""
	self.release += 1

    def incrementBuildCount(self):
	"""
	Incremements the build count
	"""
	if self.buildCount:
	    self.buildCount += 1
	else:
	    self.buildCount = 1

    def __init__(self, value):
	"""
	Initialize a VersionRelease object from a string representation
	of a version release. ParseError exceptions are thrown if the
	string representation is ill-formed.

	@param value: String representation of a VersionRelease
	@type value: string
	"""
	if value.find("@") != -1:
	    raise ParseError, "version/release pairs may not contain @ signs"
	cut = value.find("-")
	if cut == -1:
	    raise ParseError, ("version/release pair was expected")
	self.version = value[:cut]

	try:
	    int(self.version[0])
	except:
	    raise ParseError, \
		("version numbers must be begin with a digit: %s" % value)

	fullRelease = value[cut + 1:]
	cut = fullRelease.find(".")
	if cut != -1:
	    self.release = fullRelease[:cut]
	    self.buildCount = fullRelease[cut + 1:]
	else:
	    self.release = fullRelease
	    self.buildCount = None

	try:
	    self.release = int(self.release)
	except:
	    raise ParseError, ("release numbers must be all numeric: %s" % value)
	if self.buildCount:
	    try:
		self.buildCount = int(self.buildCount)
	    except:
		raise ParseError, \
		    ("build count numbers must be all numeric: %s" % value)

class BranchName(AbstractBranch):

    """
    Stores a branch name, which is the same as a nickname. Branch names
    are of the form hostname@branch.
    """

    def __str__(self):
	"""
	Returns the string representation of a branch name.
	"""
	return self.host + '@' + str(self.branch)

    def getHost(self):
	return self.host

    def equal(self, version):
	"""
	Compares the BranchName object to another object, and returns
	true if they refer to the same branch.

	@param version: version to compare against
	@type version: instance
	@rtype: boolean
	"""
	if (self.__class__ == version.__class__
	     and self.host == version.host and self.branch == version.branch):
	    return 1
	return 0

    def __init__(self, value):
	"""
	Parses a branch name string into a BranchName object. A ParseError is
	thrown if the BranchName is not well formed.

	@param value: String representation of a BranchName
	@type value: str
	"""
	if value.find("@") == -1:
	    raise ParseError, "@ expected between hostname and branch name"
	if value.find("/") != -1:
	    raise ParseError, "/ should not appear in a branch name"

	(self.host, self.branch) = value.split("@", 1)
	if self.branch.find("@") != -1:
	    raise ParseError, ("branch names may not have @ signs: %s" % value)

class LocalBranch(BranchName):

    """
    Class defining the local branch.
    """

    def __init__(self):
	BranchName.__init__(self, "localhost@LOCAL")

class Version:

    """
    Class representing a version. Versions are a list of AbstractBranch,
    AbstractVersion sequences. If the last item is an AbstractBranch (meaning
    an odd number of objects are in the list, the version represents
    a branch. A version includes a time stamp, which is used for
    ordering.
    """

    def appendVersionRelease(self, version, release):
	"""
	Converts a branch to a version. The version/release passed in
	are converted to a VersionRelease object and appended to the
	branch this object represented. The time stamp is reset as
	a new version has been created.

	@param version: string representing a version
	@type version: str
	@param release: release number
	@type release: int
	"""
	assert(self.isBranch())
	self.versions.append(VersionRelease("%s-%d" % (version, release)))
	self.timeStamp = time.time()

    def appendVersionReleaseObject(self, verRel):
	"""
	Converts a branch to a version. The version/release passed in
	are appended to the branch this object represented. The time
	stamp is reset as a new version has been created.

	@param verRel: object for the version and release
	@type verRel: VersionRelease
	"""
	assert(self.isBranch())
	self.versions.append(verRel)
	self.timeStamp = time.time()

    def incrementRelease(self):
	"""
	The release number for the final element in the version is
	incremented by one and the time stamp is reset.
	"""
	assert(self.isVersion())

	self.versions[-1].incrementRelease()
	self.timeStamp = time.time()

    def incrementBuildCount(self):
	"""
	The build count number for the final element in the version is
	incremented by one and the time stamp is reset.
	"""
	assert(self.isVersion())

	self.versions[-1].incrementBuildCount()
	self.timeStamp = time.time()

    def trailingVersion(self):
	"""
	Returns the AbstractVersion object at the end of the version.

	@rtype: AbstactVersion
	"""
	assert(self.isVersion())

	return self.versions[-1]

    def _listsEqual(self, list, other):
	if len(other.versions) != len(list): return 0

	for i in range(0, len(list)):
	    if not list[i].equal(other.versions[i]): return 0

	return 1

    def equal(self, other):
	"""
	Compares this object to another Version object to see if they
	are the same.

	@rtype: boolean
	"""
	return self._listsEqual(self.versions, other)

    def asString(self, defaultBranch = None):
	"""
	Returns a string representation of the version.

	@param defaultBranch: If set this is stripped fom the beginning
	of the version to give a shorter string representation.
	@type defaultBranch: Version
	@rtype: str
	"""
	list = self.versions
	s = "/"

	if defaultBranch and len(defaultBranch.versions) < len(self.versions):
	    start = Version(self.versions[0:len(defaultBranch.versions)], 0)
	    if start.equal(defaultBranch):
		list = self.versions[len(defaultBranch.versions):]
		s = ""

	for version in list:
	    s = s + ("%s/" % version)

	return s[:-1]

    def freeze(self, defaultBranch = None):
	"""
	Returns a complete string representation of the version, including
	the time stamp.

	@rtype: str
	"""
	return ("%.3f:" % self.timeStamp) + self.asString(defaultBranch)

    def isBranch(self):
	"""
	Tests whether or not the current object is a branch.

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], BranchName)

    def isVersion(self):
	"""
	Tests whether or not the current object is a version (not a branch).

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], VersionRelease)

    def isLocal(self):
    	"""
	Tests whether this is the local branch, or is a version on
	the local branch

	@rtype: boolean
	"""
	return isinstance(self.versions[-1], LocalBranch) or    \
	    (len(self.versions) > 1 and
	     isinstance(self.versions[-2], LocalBranch))

    def onBranch(self, branch):
	"""
	Tests whether or not the current object is a version on the
	specified branch.

	@rtype: boolean
	"""
	if self.isBranch(): return 0
	return self._listsEqual(self.versions[:-1], branch)

    def branch(self):
	"""
	Returns the branch this version is part of.

	@rtype: Version
	"""
	assert(not self.isBranch())
	return Version(self.versions[:-1], 0)

    def branchNickname(self):
	"""
	Returns the BranchName object at the end of a branch. This is
	known as the branch nick name, as is used in VersionedFiles as
	an index.

	@rtype: BranchName
	"""
	assert(self.isBranch())
	return self.versions[-1]

    def parent(self):
	"""
	Returns the parent version for this version (the version this
	object's branch branched from.

	@rtype: Version
	"""
	assert(self.isVersion())
	assert(len(self.versions) > 3)
	return Version(self.versions[:-2], 0)

    def parentNode(self):
	"""
	Returns the parent version of a branch.

	@rtype: Version
	"""
	assert(self.isBranch())
	assert(len(self.versions) >= 3)
	return Version(self.versions[:-1], 0)

    def hasParent(self):
	"""
	Tests whether or not the current branch or version has a parent.
	True for all versions other then those on trunks.

	@rtype: boolean
	"""
	return(len(self.versions) >= 3)

    def isBefore(self, other):
	"""
	Tests whether the parameter is a version earlier then this object.

	@param other: Object to test against
	@type other: Version
	@rtype: boolean
	"""
	return self.timeStamp < other.timeStamp

    def isAfter(self, other):
	"""
	Tests whether the parameter is a version later then this object.

	@param other: Object to test against
	@type other: Version
	@rtype: boolean
	"""
	return self.timeStamp > other.timeStamp

    def copy(self):
	"""
	Returns a Version object which is a copy of this object. The
	result can be modified without affecting this object in any way.j

	@rtype: Version
	"""

        return copy.deepcopy(self)

    def fork(self, branch, sameVerRel = True):
	"""
	Creates a new branch from this version.

	@param branch: Branch to create for this version
	@type branch: AbstractBranch
	@param sameVerRel: If set, the new branch is turned into a version
	on the branch using the same version and release as the original
	verison.
	@type sameVerRel: boolean
	@rtype: Version
	"""
	assert(isinstance(branch, AbstractBranch))
	newlist = [ branch ]

	if sameVerRel:
	    newlist.append(self.versions[-1])

	return Version(self.versions + newlist, time.time())

    def parseVersionString(self, ver, defaultBranch = None):
	"""
	Converts a string representation of a version into a VersionRelease
	object.

	@param ver: version string
	@type ver: str
	@param defaultBranch: if provided and the ver parameter is not
	fully-qualified (it doesn't begin with a /), ver is taken to
	be relative to this branch.
	@type defaultBranch: Version
	"""
	if ver[0] != "/":
            # XXX broken code, no defaultBranch in this scope
	    if not defaultBranch:
		raise KeyError, "relative version given without a default " \
			        "branch"
	    ver = defaultBranch.asString() + "/" + ver

	parts = ver.split("/")
	del parts[0]	# absolute versions start with a /

	v = []
	while parts:
	    if parts[0] == "localhost@LOCAL":
		v.append(LocalBranch())
	    else:
		v.append(BranchName(parts[0]))

	    if len(parts) >= 2:
		v.append(VersionRelease(parts[1]))
		parts = parts[2:]
	    else:
		parts = None

	return v

    """
    Creates a Version object from a list of AbstractBranch and AbstractVersion
    objects.
    """
    def __init__(self, versionList, timeStamp):
	self.versions = versionList
	self.timeStamp = timeStamp

def ThawVersion(ver):
    if ver == "@NEW@":
	return NewVersion()
    return _ThawVersion(ver)

class _ThawVersion(Version):

    """
    Provides a version object from a frozen version string.
    """

    def __init__(self, fullString):
	"""
	Initializes a ThawVersion object.

	@param fullString: Frozen representation of a Version object.
	@type fullString: str
	"""
	(timeStr, ver) = fullString.split(":")

	timeVal = float(timeStr)
	v = self.parseVersionString(ver)

	Version.__init__(self, v, timeVal)

def VersionFromString(ver, defaultBranch = None):
    if ver == "@NEW@":
	return NewVersion()
    return _VersionFromString(ver, defaultBranch)

class _VersionFromString(Version):

    """
    Provides a version object from a string representation of a version.
    The time stamp is set to 0, so this object cannot be properly ordered
    with respect to other versions.
    """

    def __init__(self, ver, defaultBranch = None):
	"""
	Initializes a VersionFromString object.

	@param ver: string representation of a version
	@type ver: str
	@param defaultBranch: if provided and the ver parameter is not
	fully-qualified (it doesn't begin with a /), ver is taken to
	be relative to this branch.
	@type defaultBranch: Version
	"""
	if ver[0] == "@NEW@":
	    return NewVersion()

	if ver[0] != "/":
	    ver = defaultBranch.asString() + "/" + ver

	v = self.parseVersionString(ver, defaultBranch)

	Version.__init__(self, v, 0)

class VersionsError(Exception):

    """
    Ancestor for all exceptions raised by the versions module.
    """

    pass

class ParseError(VersionsError):

    """
    Indicates that an error occured turning a string into an object
    in the versions module.
    """

    def __repr__(self):
	return self.str

    def __str__(self):
	return self.str

    def __init__(self, str):
	self.str = str
<|endoftext|>


<commit_before>import os
import glob
from pathlib import Path

import flask
import yaml

class VersionManager(object):
    def __init__(self, versions=None):
        self._versions = versions

    @property
    def versions(self):
        if not self._versions:
            version_paths = Path('versions').glob('**/*.yaml')
            version_files = map(str, version_paths)
            versions = map(Version.fromfile, version_files)
            versions = sorted(versions, key=lambda v: v.version)
            self._versions = list(versions)
        return self._versions

    def all(self):
        return self

    def filter(self, version=None, snapshots=None, platform=None):
        versions = self.versions

        if version:
            versions = [v for v in versions if v.version == version]

        if snapshots is True:
            versions = [v for v in versions if v.is_snapshot]

        if snapshots is False:
            versions = [v for v in versions if not v.is_snapshot]

        if platform:
            versions = [v for v in versions if not v.supports_platform(platform)]

        return VersionManager(versions)

    def get(self, **kwargs):
        if kwargs:
            versions = self.filter(**kwargs)
            return versions.get()

        if len(self.versions) == 1:
            return self.versions[0]

        raise flask.abort(404)

class Version(object):
    objects = VersionManager()

    @classmethod
    def fromfile(cls, path):
        version = os.path.splitext(os.path.basename(path))[0]

        with open(path) as fp:
            content = yaml.load(fp.read())
            binaries = content['binaries']

        if 'version' in content:
            version = content['version']

        return cls(version, binaries)

    def __init__(self, version, binaries):
        self.version = version
        self.binaries = binaries

    def __str__(self):
        return self.version

    def __eq__(self, other):
        if isinstance(other, Version):
            return self.version == other.version and self.binaries == other.binaries

        return False

    @property
    def is_snapshot(self):
        return 'SNAPSHOT' in self.version

    def supports_platform(self, platform):
        """
        Returns if the version has a binary release for the given platform.
        """
        return platform in self.binaries

    @property
    def path(self):
        if self.version.startswith('DEVELOPMENT-SNAPSHOT-'):
            version = self.version[len('DEVELOPMENT-SNAPSHOT-'):]
            (year, month, rest) = version.split('-', 2)
            return os.path.join('versions', 'DEVELOPMENT-SNAPSHOT', year, month, '{}.yaml'.format(rest))

        if '-' in self.version:
            version, rest = self.version.split('-', 1)
        else:
            version = self.version
            rest = None

        major = version.split('.', 1)[0]

        if rest:
            if rest.startswith('DEVELOPMENT-SNAPSHOT-'):
                rest = rest[len('DEVELOPMENT-SNAPSHOT-'):]
                return os.path.join('versions', major, 'DEVELOPMENT-SNAPSHOT', '{}.yaml'.format(rest))

        return os.path.join('versions', major, '{}.yaml'.format(self.version))

    def save(self):
        path = Path(os.path.split(self.path)[0])
        path.mkdir(parents=True, exist_ok=True)

        with open(self.path, 'w') as fp:
            yaml.dump({'version': self.version, 'binaries': self.binaries}, fp, default_flow_style=False)
<commit_msg>Fix inverted `platform` parameter handling in `versions` API (#44)<commit_after>import os
import glob
from pathlib import Path

import flask
import yaml

class VersionManager(object):
    def __init__(self, versions=None):
        self._versions = versions

    @property
    def versions(self):
        if not self._versions:
            version_paths = Path('versions').glob('**/*.yaml')
            version_files = map(str, version_paths)
            versions = map(Version.fromfile, version_files)
            versions = sorted(versions, key=lambda v: v.version)
            self._versions = list(versions)
        return self._versions

    def all(self):
        return self

    def filter(self, version=None, snapshots=None, platform=None):
        versions = self.versions

        if version:
            versions = [v for v in versions if v.version == version]

        if snapshots is True:
            versions = [v for v in versions if v.is_snapshot]

        if snapshots is False:
            versions = [v for v in versions if not v.is_snapshot]

        if platform:
            versions = [v for v in versions if v.supports_platform(platform)]

        return VersionManager(versions)

    def get(self, **kwargs):
        if kwargs:
            versions = self.filter(**kwargs)
            return versions.get()

        if len(self.versions) == 1:
            return self.versions[0]

        raise flask.abort(404)

class Version(object):
    objects = VersionManager()

    @classmethod
    def fromfile(cls, path):
        version = os.path.splitext(os.path.basename(path))[0]

        with open(path) as fp:
            content = yaml.load(fp.read())
            binaries = content['binaries']

        if 'version' in content:
            version = content['version']

        return cls(version, binaries)

    def __init__(self, version, binaries):
        self.version = version
        self.binaries = binaries

    def __str__(self):
        return self.version

    def __eq__(self, other):
        if isinstance(other, Version):
            return self.version == other.version and self.binaries == other.binaries

        return False

    @property
    def is_snapshot(self):
        return 'SNAPSHOT' in self.version

    def supports_platform(self, platform):
        """
        Returns if the version has a binary release for the given platform.
        """
        return platform in self.binaries

    @property
    def path(self):
        if self.version.startswith('DEVELOPMENT-SNAPSHOT-'):
            version = self.version[len('DEVELOPMENT-SNAPSHOT-'):]
            (year, month, rest) = version.split('-', 2)
            return os.path.join('versions', 'DEVELOPMENT-SNAPSHOT', year, month, '{}.yaml'.format(rest))

        if '-' in self.version:
            version, rest = self.version.split('-', 1)
        else:
            version = self.version
            rest = None

        major = version.split('.', 1)[0]

        if rest:
            if rest.startswith('DEVELOPMENT-SNAPSHOT-'):
                rest = rest[len('DEVELOPMENT-SNAPSHOT-'):]
                return os.path.join('versions', major, 'DEVELOPMENT-SNAPSHOT', '{}.yaml'.format(rest))

        return os.path.join('versions', major, '{}.yaml'.format(self.version))

    def save(self):
        path = Path(os.path.split(self.path)[0])
        path.mkdir(parents=True, exist_ok=True)

        with open(self.path, 'w') as fp:
            yaml.dump({'version': self.version, 'binaries': self.binaries}, fp, default_flow_style=False)
<|endoftext|>


<commit_before>"""Main command-line entry-point and any code tightly coupled to it.
"""
import sys
import os
import argparse
from vex import config
from vex.run import get_environ, run

class InvalidArgument(Exception):
    """Raised by anything under main() to propagate errors to user.
    """
    def __init__(self, message):
        self.message = message
        Exception.__init__(self, message)

class NoVirtualenvName(InvalidArgument):
    """No virtualenv name was given (insufficient arguments).
    """
    pass

class NoVirtualenvsDirectory(InvalidArgument):
    """There is no directory to find named virtualenvs in.
    """
    pass

class OtherShell(InvalidArgument):
    """The given argument to --shell-config is not recognized.
    """
    pass

class UnknownArguments(InvalidArgument):
    """Unknown arguments were given on the command line.

    This is a byproduct of having to use parse_known_args.
    """
    pass

class InvalidVexrc(InvalidArgument):
    """config file specified or required but absent or unparseable.
    """
    pass

class InvalidVirtualenv(InvalidArgument):
    """No usable virtualenv was found.
    """
    pass

class InvalidCommand(InvalidArgument):
    """No runnable command was found.
    """
    pass

class InvalidCwd(InvalidArgument):
    """cwd specified or required but unusable.
    """
    pass

def make_arg_parser():
    """Return a standard ArgumentParser object.
    """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        usage="vex [OPTIONS] VIRTUALENV_NAME COMMAND_TO_RUN ...",
    )

    parser.add_argument(
        "--path",
        metavar="DIR",
        help="absolute path to virtualenv to use",
        action="store"
    )
    parser.add_argument(
        '--cwd',
        metavar="DIR",
        action="store",
        default='.',
        help="path to run command in (default: '.' aka $PWD)",
    )
    parser.add_argument(
        "--config",
        metavar="FILE",
        default=None,
        action="store",
        help="path to config file to read (default: '~/.vexrc')"
    )
    parser.add_argument(
        '--shell-config',
        metavar="SHELL",
        dest="shell_to_configure",
        action="store",
        default=None,
        help="print optional config for the specified shell"
    )
    parser.add_argument(
        "rest",
        nargs=argparse.REMAINDER,
        help=argparse.SUPPRESS)

    return parser

def get_options(argv):
    """Called to parse the given list as command-line arguments.

    :returns:
        an options object as returned by argparse.
    """
    arg_parser = make_arg_parser()
    options, unknown = arg_parser.parse_known_args(argv)
    if unknown:
        arg_parser.print_help()
        raise UnknownArguments("unknown args: {0!r}".format(unknown))
    options.print_help = arg_parser.print_help
    return options

def get_vexrc(options, environ):
    """Get a representation of the contents of the config file.

    :returns:
        a Vexrc instance.
    """
    # Complain if user specified nonexistent file with --config.
    # But we don't want to complain just because ~/.vexrc doesn't exist.
    if options.config and not os.path.exists(options.config):
        raise InvalidVexrc("nonexistent config: {0!r}".format(options.config))
    filename = options.config or os.path.expanduser('~/.vexrc')
    vexrc = config.Vexrc.from_file(filename, environ)
    return vexrc

def handle_shell_config(options, vexrc, environ):
    """Carry out the logic of the --shell-config option.
    """
    from vex import shell_config
    data = shell_config.shell_config_for(
        options.shell_to_configure, vexrc, environ)
    if not data:
        raise OtherShell("unknown shell: " + options.shell_to_configure)
    if hasattr(sys.stdout, 'buffer'):
        sys.stdout.buffer.write(data)
    else:
        sys.stdout.write(data)
    return 0

def get_cwd(options):
    """Discover what directory the command should run in.
    """
    if not options.cwd:
        return None
    if not os.path.exists(options.cwd):
        raise InvalidCwd(
            "can't --cwd to invalid path {0!r}".format(options.cwd))
    return options.cwd

def get_virtualenv_path(options, vexrc, environ):
    """Find a virtualenv path.
    """
    if options.path:
        ve_path = options.path
    else:
        ve_base = vexrc.get_ve_base(environ)
        if not ve_base:
            raise NoVirtualenvsDirectory(
                "could not figure out a virtualenvs directory. "
                "make sure $HOME is set, or $WORKON_HOME,"
                " or set virtualenvs=something in your .vexrc")
        # Using this requires get_ve_base to pass through nonexistent dirs
        if not os.path.exists(ve_base):
            raise NoVirtualenvsDirectory(
                "virtualenvs directory {0!r} not found.".format(ve_base))
        ve_name = options.rest.pop(0) if options.rest else ''
        if not ve_name:
            raise NoVirtualenvName(
                "could not find a virtualenv name in the command line."
            )

        # n.b.: if ve_name is absolute, ve_base is discarded by os.path.join,
        # and an absolute path will be accepted as first arg.
        # So we check if they gave an absolute path as ve_name.
        # But we don't want this error if $PWD == $WORKON_HOME,
        # in which case 'foo' is a valid relative path to virtualenv foo.
        ve_path = os.path.join(ve_base, ve_name)
        if ve_path == ve_name and os.path.basename(ve_name) != ve_name:
            raise InvalidVirtualenv(
                'To run in a virtualenv by its path, '
                'use "vex --path {0}"'.format(ve_path))

    ve_path = os.path.abspath(ve_path)
    if not os.path.exists(ve_path):
        raise InvalidVirtualenv("no virtualenv found at {0!r}.".format(ve_path))
    return ve_path

def get_command(options, vexrc, environ):
    """Get a command to run.

    :returns:
        a list of strings representing a command to be passed to Popen.
    """
    command = options.rest
    if not command:
        command = vexrc.get_shell(environ)
    if command and command[0].startswith('--'):
        raise InvalidCommand(
            "don't put flags like '%s' after the virtualenv name."
            % command[0])
    if not command:
        raise InvalidCommand("no command given")
    return command

def _main(environ, argv):
    """Logic for main(), with less direct system interaction.

    Routines called here raise InvalidArgument with messages that
    should be delivered on stderr, to be caught by main.
    """
    options = get_options(argv)
    vexrc = get_vexrc(options, environ)
    # Handle --shell-config as soon as its arguments are available.
    if options.shell_to_configure:
        return handle_shell_config(options, vexrc, environ)
    cwd = get_cwd(options)
    try:
        ve_path = get_virtualenv_path(options, vexrc, environ)
    except NoVirtualenvName:
        options.print_help()
        raise
    command = get_command(options, vexrc, environ)
    # get_environ has to wait until ve_path is defined, which might
    # be after a make; of course we can't run until we have env.
    env = get_environ(environ, vexrc['env'], ve_path)
    returncode = run(command, env=env, cwd=cwd)
    if returncode is None:
        raise InvalidCommand("command not found: {0!r}".format(command[0]))
    return returncode

def main():
    """The main command-line entry point, with system interactions.
    """
    argv = sys.argv[1:]
    returncode = 1
    try:
        returncode = _main(os.environ, argv)
    except NoVirtualenvName:
        sys.stderr.write("\n")
        sys.stderr.write("Error: no virtualenv name found in command line.\n")
    except InvalidArgument as error:
        if error.message:
            sys.stderr.write("Error: " + error.message + '\n')
    sys.exit(returncode)
<commit_msg>simplify exception handling in main<commit_after>"""Main command-line entry-point and any code tightly coupled to it.
"""
import sys
import os
import argparse
from vex import config
from vex.run import get_environ, run

class InvalidArgument(Exception):
    """Raised by anything under main() to propagate errors to user.
    """
    def __init__(self, message):
        self.message = message
        Exception.__init__(self, message)

class NoVirtualenvName(InvalidArgument):
    """No virtualenv name was given (insufficient arguments).
    """
    pass

class NoVirtualenvsDirectory(InvalidArgument):
    """There is no directory to find named virtualenvs in.
    """
    pass

class OtherShell(InvalidArgument):
    """The given argument to --shell-config is not recognized.
    """
    pass

class UnknownArguments(InvalidArgument):
    """Unknown arguments were given on the command line.

    This is a byproduct of having to use parse_known_args.
    """
    pass

class InvalidVexrc(InvalidArgument):
    """config file specified or required but absent or unparseable.
    """
    pass

class InvalidVirtualenv(InvalidArgument):
    """No usable virtualenv was found.
    """
    pass

class InvalidCommand(InvalidArgument):
    """No runnable command was found.
    """
    pass

class InvalidCwd(InvalidArgument):
    """cwd specified or required but unusable.
    """
    pass

def make_arg_parser():
    """Return a standard ArgumentParser object.
    """
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawTextHelpFormatter,
        usage="vex [OPTIONS] VIRTUALENV_NAME COMMAND_TO_RUN ...",
    )

    parser.add_argument(
        "--path",
        metavar="DIR",
        help="absolute path to virtualenv to use",
        action="store"
    )
    parser.add_argument(
        '--cwd',
        metavar="DIR",
        action="store",
        default='.',
        help="path to run command in (default: '.' aka $PWD)",
    )
    parser.add_argument(
        "--config",
        metavar="FILE",
        default=None,
        action="store",
        help="path to config file to read (default: '~/.vexrc')"
    )
    parser.add_argument(
        '--shell-config',
        metavar="SHELL",
        dest="shell_to_configure",
        action="store",
        default=None,
        help="print optional config for the specified shell"
    )
    parser.add_argument(
        "rest",
        nargs=argparse.REMAINDER,
        help=argparse.SUPPRESS)

    return parser

def get_options(argv):
    """Called to parse the given list as command-line arguments.

    :returns:
        an options object as returned by argparse.
    """
    arg_parser = make_arg_parser()
    options, unknown = arg_parser.parse_known_args(argv)
    if unknown:
        arg_parser.print_help()
        raise UnknownArguments("unknown args: {0!r}".format(unknown))
    options.print_help = arg_parser.print_help
    return options

def get_vexrc(options, environ):
    """Get a representation of the contents of the config file.

    :returns:
        a Vexrc instance.
    """
    # Complain if user specified nonexistent file with --config.
    # But we don't want to complain just because ~/.vexrc doesn't exist.
    if options.config and not os.path.exists(options.config):
        raise InvalidVexrc("nonexistent config: {0!r}".format(options.config))
    filename = options.config or os.path.expanduser('~/.vexrc')
    vexrc = config.Vexrc.from_file(filename, environ)
    return vexrc

def handle_shell_config(options, vexrc, environ):
    """Carry out the logic of the --shell-config option.
    """
    from vex import shell_config
    data = shell_config.shell_config_for(
        options.shell_to_configure, vexrc, environ)
    if not data:
        raise OtherShell("unknown shell: " + options.shell_to_configure)
    if hasattr(sys.stdout, 'buffer'):
        sys.stdout.buffer.write(data)
    else:
        sys.stdout.write(data)
    return 0

def get_cwd(options):
    """Discover what directory the command should run in.
    """
    if not options.cwd:
        return None
    if not os.path.exists(options.cwd):
        raise InvalidCwd(
            "can't --cwd to invalid path {0!r}".format(options.cwd))
    return options.cwd

def get_virtualenv_path(options, vexrc, environ):
    """Find a virtualenv path.
    """
    if options.path:
        ve_path = options.path
    else:
        ve_base = vexrc.get_ve_base(environ)
        if not ve_base:
            raise NoVirtualenvsDirectory(
                "could not figure out a virtualenvs directory. "
                "make sure $HOME is set, or $WORKON_HOME,"
                " or set virtualenvs=something in your .vexrc")
        # Using this requires get_ve_base to pass through nonexistent dirs
        if not os.path.exists(ve_base):
            raise NoVirtualenvsDirectory(
                "virtualenvs directory {0!r} not found.".format(ve_base))
        ve_name = options.rest.pop(0) if options.rest else ''
        if not ve_name:
            raise NoVirtualenvName(
                "could not find a virtualenv name in the command line."
            )

        # n.b.: if ve_name is absolute, ve_base is discarded by os.path.join,
        # and an absolute path will be accepted as first arg.
        # So we check if they gave an absolute path as ve_name.
        # But we don't want this error if $PWD == $WORKON_HOME,
        # in which case 'foo' is a valid relative path to virtualenv foo.
        ve_path = os.path.join(ve_base, ve_name)
        if ve_path == ve_name and os.path.basename(ve_name) != ve_name:
            raise InvalidVirtualenv(
                'To run in a virtualenv by its path, '
                'use "vex --path {0}"'.format(ve_path))

    ve_path = os.path.abspath(ve_path)
    if not os.path.exists(ve_path):
        raise InvalidVirtualenv("no virtualenv found at {0!r}.".format(ve_path))
    return ve_path

def get_command(options, vexrc, environ):
    """Get a command to run.

    :returns:
        a list of strings representing a command to be passed to Popen.
    """
    command = options.rest
    if not command:
        command = vexrc.get_shell(environ)
    if command and command[0].startswith('--'):
        raise InvalidCommand(
            "don't put flags like '%s' after the virtualenv name."
            % command[0])
    if not command:
        raise InvalidCommand("no command given")
    return command

def _main(environ, argv):
    """Logic for main(), with less direct system interaction.

    Routines called here raise InvalidArgument with messages that
    should be delivered on stderr, to be caught by main.
    """
    options = get_options(argv)
    vexrc = get_vexrc(options, environ)
    # Handle --shell-config as soon as its arguments are available.
    if options.shell_to_configure:
        return handle_shell_config(options, vexrc, environ)
    cwd = get_cwd(options)
    try:
        ve_path = get_virtualenv_path(options, vexrc, environ)
    except NoVirtualenvName:
        options.print_help()
        raise
    command = get_command(options, vexrc, environ)
    # get_environ has to wait until ve_path is defined, which might
    # be after a make; of course we can't run until we have env.
    env = get_environ(environ, vexrc['env'], ve_path)
    returncode = run(command, env=env, cwd=cwd)
    if returncode is None:
        raise InvalidCommand("command not found: {0!r}".format(command[0]))
    return returncode

def main():
    """The main command-line entry point, with system interactions.
    """
    argv = sys.argv[1:]
    returncode = 1
    try:
        returncode = _main(os.environ, argv)
    except InvalidArgument as error:
        if error.message:
            sys.stderr.write("Error: " + error.message + '\n')
        else:
            raise
    sys.exit(returncode)
<|endoftext|>


<commit_before>import os

from setuptools import setup, find_packages

here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.txt')) as f:
    README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
    CHANGES = f.read()

requires = [
    'pyramid',
    'pyramid_chameleon',
    'pyramid_debugtoolbar',
    'pyramid_tm',
    'SQLAlchemy',
    'transaction',
    'zope.sqlalchemy',
    'waitress',
    'psycopg2',
    ]

tests_require = ['pytest', 'pytest-watch', 'tox']

dev_requires = ['ipython', 'pyramid-ipython']

setup(name='journal',
      version='0.0',
      description='journal',
      long_description=README + '\n\n' + CHANGES,
      classifiers=[
        "Programming Language :: Python",
        "Framework :: Pyramid",
        "Topic :: Internet :: WWW/HTTP",
        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
        ],
      author='',
      author_email='',
      url='',
      keywords='web wsgi bfg pylons pyramid',
      packages=find_packages(),
      include_package_data=True,
      zip_safe=False,
      test_suite='journal',
      install_requires=requires,
      extras_require={
        'test': tests_require,
        'dev' : dev_requires,
      }
      entry_points="""\
      [paste.app_factory]
      main = journal:main
      [console_scripts]
      initialize_db = journal.scripts.initializedb:main
      """,
      )
<commit_msg>added a comma to setup so tox passes.<commit_after>import os

from setuptools import setup, find_packages

here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.txt')) as f:
    README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
    CHANGES = f.read()

requires = [
    'pyramid',
    'pyramid_chameleon',
    'pyramid_debugtoolbar',
    'pyramid_tm',
    'SQLAlchemy',
    'transaction',
    'zope.sqlalchemy',
    'waitress',
    'psycopg2',
    ]

tests_require = ['pytest', 'pytest-watch', 'tox']

dev_requires = ['ipython', 'pyramid-ipython']

setup(name='journal',
      version='0.0',
      description='journal',
      long_description=README + '\n\n' + CHANGES,
      classifiers=[
        "Programming Language :: Python",
        "Framework :: Pyramid",
        "Topic :: Internet :: WWW/HTTP",
        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
        ],
      author='',
      author_email='',
      url='',
      keywords='web wsgi bfg pylons pyramid',
      packages=find_packages(),
      include_package_data=True,
      zip_safe=False,
      test_suite='journal',
      install_requires=requires,
      extras_require={
        'test': tests_require,
        'dev' : dev_requires,
      },
      entry_points="""\
      [paste.app_factory]
      main = journal:main
      [console_scripts]
      initialize_db = journal.scripts.initializedb:main
      """,
      )
<|endoftext|>