Changeset - c10b5fb50f4f
[Not reviewed]
0 44 0
Lance Edgar (lance) - 7 years ago 2018-02-12 14:55:15
lance@edbob.org
Misc. cleanup for Python 3
44 files changed with 174 insertions and 150 deletions:
0 comments (0 inline, 0 general)
rattail/barcodes.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -28,6 +28,8 @@ from __future__ import unicode_literals, absolute_import
 

	
 
# import re
 

	
 
import six
 

	
 

	
 
def upc_check_digit(data):
 
    """
 
@@ -58,7 +60,7 @@ def luhn_check_digit(data):
 
        digit = int(reverse_data[i])
 
        if i % 2 == 0:
 
            digit *= 2
 
            digit = sum([int(x) for x in str(digit)])
 
            digit = sum([int(x) for x in six.text_type(digit)])
 
        sum_ += digit
 
    remainder = sum_ % 10
 
    if remainder == 0:
 
@@ -81,8 +83,8 @@ def price_check_digit(data):
 

	
 
    .. _Price Check Digit algorithm: http://barcodes.gs1us.org/GS1%20US%20BarCodes%20and%20eCom%20-%20The%20Global%20Language%20of%20Business.htm#PCD
 
    """
 
    if not isinstance(data, basestring) or len(data) != 4:
 
        raise ValueError("'data' must be 4-character string; got: %s" % str(data))
 
    if not isinstance(data, six.string_types) or len(data) != 4:
 
        raise ValueError("'data' must be 4-character string; got: {}".format(data))
 

	
 
    map1 = {0:0, 1:2, 2:4, 3:6, 4:8, 5:9, 6:1, 7:3, 8:5, 9:7}
 
    map2 = {0:0, 1:3, 2:6, 3:9, 4:2, 5:5, 6:8, 7:1, 8:4, 9:7}
 
@@ -90,7 +92,7 @@ def price_check_digit(data):
 

	
 
    data = [int(x) for x in data]
 
    sum_ = map1[data[0]] + map1[data[1]] + map2[data[2]] + map3[data[3]]
 
    return int(str(3 * sum_)[-1])
 
    return int(six.text_type(3 * sum_)[-1])
 

	
 

	
 
def upce_to_upca(data, include_check_digit=False):
 
@@ -142,7 +144,7 @@ def upce_to_upca(data, include_check_digit=False):
 
        upca = "0%05u00009" % int(upce[0:5])
 

	
 
    if include_check_digit:
 
        upca += str(upc_check_digit(upca))
 
        upca += six.text_type(upc_check_digit(upca))
 

	
 
    return upca
 

	
rattail/batch/labels.py
Show inline comments
 
@@ -280,7 +280,7 @@ class LabelBatchHandler(BatchHandler):
 
        # okay now print for real
 
        # TODO: this is compatible with legacy code but will re-read all needed
 
        # product data from master table instead of levaraging batch rows
 
        for profile in profiles.itervalues():
 
        for profile in profiles.values():
 
            printer = profile.get_printer(self.config)
 
            printer.print_labels(profile.labels, progress=progress)
 

	
rattail/bouncer/daemon.py
Show inline comments
 
@@ -54,7 +54,7 @@ class BouncerDaemon(Daemon):
 
        Starts watcher and worker threads according to configuration.
 
        """
 
        watched = load_profiles(self.config)
 
        for key, profile in watched.iteritems():
 
        for key, profile in watched.items():
 

	
 
            # Create a thread for watching the IMAP folder.
 
            watcher = IMAPWatcher(profile)
rattail/commands/core.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -1004,14 +1004,14 @@ class PalmCommand(Subcommand):
 
        if args.subcommand == 'register':
 
            try:
 
                palm.register_conduit()
 
            except PalmError, error:
 
                sys.stderr.write(str(error) + '\n')
 
            except PalmError as error:
 
                sys.stderr.write("{}\n".format(error))
 

	
 
        elif args.subcommand == 'unregister':
 
            try:
 
                palm.unregister_conduit()
 
            except PalmError, error:
 
                sys.stderr.write(str(error) + '\n')
 
            except PalmError as error:
 
                sys.stderr.write("{}\n".format(error))
 
                
 

	
 
class Upgrade(Subcommand):
rattail/config.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -31,11 +31,13 @@ import re
 
import sys
 
import shlex
 
import datetime
 
import ConfigParser
 
from six.moves import configparser
 
import warnings
 
import logging
 
import logging.config
 

	
 
import six
 

	
 
from rattail.util import load_entry_points, import_module_path
 
from rattail.exceptions import WindowsExtensionsNotInstalled, ConfigurationError
 
from rattail.files import temp_path
 
@@ -67,14 +69,14 @@ def parse_list(value):
 
        return []
 
    # Per the shlex docs (https://docs.python.org/2/library/shlex.html):
 
    # "Prior to Python 2.7.3, this module did not support Unicode input."
 
    if isinstance(value, unicode):
 
    if six.PY2 and isinstance(value, six.text_type):
 
        value = value.encode('utf-8')
 
    parser = shlex.shlex(value)
 
    parser.whitespace += u','
 
    parser.whitespace += ','
 
    parser.whitespace_split = True
 
    values = list(parser)
 
    for i, value in enumerate(values):
 
        if value.startswith(u'"') and value.endswith(u'"'):
 
        if value.startswith('"') and value.endswith('"'):
 
            values[i] = value[1:-1]
 
    return values
 

	
 
@@ -108,7 +110,7 @@ class RattailConfig(object):
 
    def __init__(self, files=[], usedb=None, preferdb=None):
 
        self.files_requested = []
 
        self.files_read = []
 
        self.parser = ConfigParser.SafeConfigParser()
 
        self.parser = configparser.SafeConfigParser()
 
        for path in files:
 
            self.read_file(path)
 
        self.usedb = usedb
 
@@ -146,7 +148,7 @@ class RattailConfig(object):
 
        log.debug("will attempt to read config from file: {0}".format(path))
 
        self.files_requested.append(path)
 

	
 
        parser = ConfigParser.SafeConfigParser(dict(
 
        parser = configparser.SafeConfigParser(dict(
 
            here=os.path.dirname(path),
 
        ))
 
        if not parser.read(path):
 
@@ -189,7 +191,7 @@ class RattailConfig(object):
 

	
 
        try:
 
            logging.config.fileConfig(path, disable_existing_loggers=False)
 
        except ConfigParser.NoSectionError as error:
 
        except configparser.NoSectionError as error:
 
            log.warning("tried to configure logging, but got NoSectionError: {0}".format(error))
 
        else:
 
            log.debug("configured logging")
 
@@ -311,7 +313,7 @@ class RattailConfig(object):
 
        value = self.get(*args, **kwargs)
 
        if value is None:
 
            return None
 
        if isinstance(value, basestring):
 
        if isinstance(value, six.string_types):
 
            return parse_list(value)
 
        return value            # maybe a caller-provided default?
 

	
 
@@ -535,7 +537,7 @@ def make_config(files=None, usedb=None, preferdb=None, env=os.environ, winsvc=No
 
            files = files.split(os.pathsep)
 
        else:
 
            files = default_system_paths() + default_user_paths()
 
    elif isinstance(files, basestring):
 
    elif isinstance(files, six.string_types):
 
        files = [files]
 

	
 
    # If making config for a Windows service, we must read the default config
 
@@ -545,7 +547,7 @@ def make_config(files=None, usedb=None, preferdb=None, env=os.environ, winsvc=No
 
    # pass parameters to a Windows service.  This way we can effectively do
 
    # just that, via config.
 
    if winsvc is not None:
 
        parser = ConfigParser.SafeConfigParser()
 
        parser = configparser.SafeConfigParser()
 
        parser.read(files)
 
        if parser.has_section('rattail.config'):
 
            key = 'winsvc.{0}'.format(winsvc)
 
@@ -573,7 +575,7 @@ def make_config(files=None, usedb=None, preferdb=None, env=os.environ, winsvc=No
 
    # Apply extra config for all available extensions.
 
    if extend:
 
        extensions = load_entry_points('rattail.config.extensions')
 
        for extension in extensions.itervalues():
 
        for extension in extensions.values():
 
            log.debug("applying '{0}' config extension".format(extension.key))
 
            extension().configure(config)
 

	
 
@@ -732,7 +734,7 @@ class FreeTDSLoggingFilter(logging.Filter):
 
            and record.levelno == logging.ERROR
 
            and record.msg == "Exception during reset or similar"
 
            and record.exc_info
 
            and self.pattern.search(str(record.exc_info[1]))):
 
            and self.pattern.search(six.text_type(record.exc_info[1]))):
 

	
 
            # Log this as a warning instead of error, to cut down on our noise.
 
            record.levelno = logging.WARNING
rattail/contrib/vendors/catalogs/dutchvalley.py
Show inline comments
 
@@ -66,7 +66,7 @@ class DutchValleyCatalogParser(CatalogParser):
 
            # Product code is in first column...but so is a lot of other
 
            # garbage.  Use strict regex to find good rows.
 
            code = sheet.cell_value(r, 0)
 
            if not isinstance(code, basestring):
 
            if not isinstance(code, six.string_types):
 
                continue
 
            if not code_pattern.match(code):
 
                continue
rattail/core.py
Show inline comments
 
@@ -46,7 +46,7 @@ class Object(object):
 
        Constructor which assigns keyword arguments as attributes.
 
        """
 

	
 
        for key, value in kwargs.iteritems():
 
        for key, value in kwargs.items():
 
            setattr(self, key, value)
 

	
 
    def __getitem__(self, key):
rattail/daemon.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 

	
 
# This code was (mostly, with some tweaks) stolen from:
 
# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
 
@@ -9,6 +9,9 @@ import sys, os, time, atexit
 
import stat
 
from signal import SIGTERM 
 

	
 
import six
 

	
 

	
 
class Daemon(object):
 
    """
 
    A generic daemon class.
 
@@ -35,7 +38,7 @@ class Daemon(object):
 
            if pid > 0:
 
                # exit first parent
 
                sys.exit(0)
 
        except OSError, e:
 
        except OSError as e:
 
            sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
 
            sys.exit(1)
 

	
 
@@ -50,7 +53,7 @@ class Daemon(object):
 
            if pid > 0:
 
                # exit from second parent
 
                sys.exit(0)
 
        except OSError, e:
 
        except OSError as e:
 
            sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
 
            sys.exit(1)
 

	
 
@@ -66,9 +69,9 @@ class Daemon(object):
 

	
 
        # write pidfile
 
        atexit.register(self.delpid)
 
        pid = str(os.getpid())
 
        pid = six.text_type(os.getpid())
 
        with open(self.pidfile, 'w+') as f:
 
            f.write('{0}\n'.format(pid))
 
            f.write('{}\n'.format(pid))
 
        os.chmod(self.pidfile, stat.S_IRUSR|stat.S_IWUSR)
 

	
 
    def delpid(self):
 
@@ -125,13 +128,13 @@ class Daemon(object):
 
            while 1:
 
                os.kill(pid, SIGTERM)
 
                time.sleep(0.1)
 
        except OSError, err:
 
            err = str(err)
 
        except OSError as err:
 
            err = six.text_type(err)
 
            if err.find("No such process") > 0:
 
                if os.path.exists(self.pidfile):
 
                    os.remove(self.pidfile)
 
            else:
 
                print str(err)
 
                print(six.text_type(err))
 
                sys.exit(1)
 

	
 
    def restart(self):
rattail/datasync/consumers.py
Show inline comments
 
@@ -118,7 +118,7 @@ class NewDataSyncImportConsumer(DataSyncConsumer):
 
        """
 
        handler = load_object(self.handler_spec)
 
        importers = self.get_importers_from_handler(handler, default_only=True)
 
        for host_name, local_name in self.model_map.iteritems():
 
        for host_name, local_name in self.model_map.items():
 
            if local_name in importers:
 
                importers[host_name] = importers[local_name]
 
        if self.skip_local_models:
 
@@ -148,7 +148,7 @@ class NewDataSyncImportConsumer(DataSyncConsumer):
 
        Process all changes, leveraging importer(s) as much as possible.
 
        """
 
        # Update all importers with current Rattail session.
 
        for importer in self.importers.itervalues():
 
        for importer in self.importers.values():
 
            importer.session = session
 

	
 
        for change in changes:
rattail/datasync/daemon.py
Show inline comments
 
@@ -53,7 +53,7 @@ class DataSyncDaemon(Daemon):
 
        """
 
        Starts watcher and consumer threads according to configuration.
 
        """
 
        for key, profile in load_profiles(self.config).iteritems():
 
        for key, profile in load_profiles(self.config).items():
 

	
 
            # Create watcher thread for the profile.
 
            name = '{0}-watcher'.format(key)
rattail/datasync/rattail.py
Show inline comments
 
@@ -295,7 +295,7 @@ class FromRattailToRattailExportConsumer(NewDataSyncImportConsumer):
 
            target_session.set_continuum_user(self.runas_username)
 

	
 
        # update all importers with current sessions
 
        for importer in self.importers.itervalues():
 
        for importer in self.importers.values():
 
            importer.host_session = session
 
            importer.session = target_session
 

	
 
@@ -341,7 +341,7 @@ class FromRattailToRattailImportConsumer(NewDataSyncImportConsumer):
 
            local_session.set_continuum_user(self.runas_username)
 

	
 
        # update all importers with current sessions
 
        for importer in self.importers.itervalues():
 
        for importer in self.importers.values():
 
            importer.host_session = self.host_session
 
            importer.session = local_session
 

	
rattail/db/cache.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -28,6 +28,7 @@ from __future__ import unicode_literals, absolute_import
 

	
 
import logging
 

	
 
import six
 
from sqlalchemy.orm import joinedload
 

	
 
from rattail.core import Object
 
@@ -114,7 +115,7 @@ class ModelCacher(object):
 
    def get_key(self, instance, normalized):
 
        if callable(self.key):
 
            return self.key(instance, normalized)
 
        if isinstance(self.key, basestring):
 
        if isinstance(self.key, six.string_types):
 
            return getattr(instance, self.key)
 
        return tuple(getattr(instance, k) for k in self.key)
 

	
rattail/db/continuum.py
Show inline comments
 
@@ -93,7 +93,7 @@ def count_versions(obj):
 

	
 
    def value(o, k):
 
        v = getattr(o, k)
 
        if isinstance(v, unicode):
 
        if isinstance(v, six.text_type):
 
            v = v.encode('utf_8')
 
        return v
 

	
rattail/db/model/batch/purchase.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -26,6 +26,7 @@ Models for purchase order batches
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
import six
 
import sqlalchemy as sa
 
from sqlalchemy import orm
 
from sqlalchemy.ext.declarative import declared_attr
 
@@ -119,6 +120,7 @@ class PurchaseBatchRow(BatchRowMixin, PurchaseItemBase, Base):
 
        """)
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PurchaseBatchCredit(PurchaseCreditBase, Base):
 
    """
 
    Represents a working copy of purchase credit tied to a batch row.
 
@@ -146,7 +148,7 @@ class PurchaseBatchCredit(PurchaseCreditBase, Base):
 
            List of :class:`PurchaseBatchCredit` instances for the row.
 
            """))
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        if self.cases_shorted is not None and self.units_shorted is not None:
 
            qty = "{} cases, {} units".format(
 
                pretty_quantity(self.cases_shorted),
rattail/db/model/datasync.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -28,11 +28,13 @@ from __future__ import unicode_literals
 

	
 
import datetime
 

	
 
import six
 
import sqlalchemy as sa
 

	
 
from rattail.db.model import Base, uuid_column
 

	
 

	
 
@six.python_2_unicode_compatible
 
class DataSyncChange(Base):
 
    """
 
    Represents a change obtained from a DataSync watcher thread, and destined
 
@@ -72,9 +74,9 @@ class DataSyncChange(Base):
 
    configured as not "isolated".
 
    """)
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        if self.payload_type and self.payload_key:
 
            return "{0}: {1}{2}".format(
 
            return "{}: {}{}".format(
 
                self.payload_type,
 
                self.payload_key,
 
                " (deletion)" if self.deletion else "")
rattail/db/model/tempmon.py
Show inline comments
 
@@ -35,6 +35,7 @@ from sqlalchemy import orm
 
from rattail.db.model import Base, uuid_column
 

	
 

	
 
@six.python_2_unicode_compatible
 
class TempmonClient(Base):
 
    """
 
    Represents a tempmon client.
 
@@ -52,7 +53,7 @@ class TempmonClient(Base):
 
    enabled = sa.Column(sa.Boolean(), nullable=False, default=False)
 
    online = sa.Column(sa.Boolean(), nullable=False, default=False)
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return '{} ({})'.format(self.config_key, self.hostname)
 

	
 
    def enabled_probes(self):
rattail/db/sync/__init__.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -24,7 +24,7 @@
 
Database Synchronization
 
"""
 

	
 
from __future__ import unicode_literals
 
from __future__ import unicode_literals, absolute_import
 

	
 
import sys
 
import time
 
@@ -80,12 +80,12 @@ class Synchronizer(object):
 
        self.remote_engines = remote_engines
 

	
 
    def loop(self):
 
        log.info("Synchronizer.loop: using remote engines: {0}".format(
 
                ', '.join(self.remote_engines.iterkeys())))
 
        log.info("using remote engines: %s",
 
                 ', '.join(self.remote_engines.keys()))
 
        while True:
 
            try:
 
                self.synchronize()
 
            except OperationalError, error:
 
            except OperationalError as error:
 
                if error.connection_invalidated:
 
                    # Presumably a database server restart; give it a moment
 
                    # and try again.
 
@@ -107,12 +107,12 @@ class Synchronizer(object):
 
            log.debug("Synchronizer.synchronize: found {0} change(s) to synchronize".format(len(local_changes)))
 

	
 
            remote_sessions = {}
 
            for key, remote_engine in self.remote_engines.iteritems():
 
            for key, remote_engine in self.remote_engines.items():
 
                remote_sessions[key] = self.Session(bind=remote_engine)
 

	
 
            self.synchronize_changes(local_changes, local_session, remote_sessions)
 

	
 
            for remote_session in remote_sessions.itervalues():
 
            for remote_session in remote_sessions.values():
 
                remote_session.commit()
 
                remote_session.close()
 
            local_session.commit()
 
@@ -138,7 +138,7 @@ class Synchronizer(object):
 
                log.debug("Synchronizer.synchronize_changes: processing change: {0}".format(repr(change)))
 

	
 
                if change.deleted:
 
                    for remote_session in remote_sessions.itervalues():
 
                    for remote_session in remote_sessions.values():
 
                        remote_instance = remote_session.query(klass).get(change.uuid)
 
                        if remote_instance:
 
                            self.delete_instance(remote_session, remote_instance)
 
@@ -147,7 +147,7 @@ class Synchronizer(object):
 
                else: # new/dirty
 
                    local_instance = local_session.query(klass).get(change.uuid)
 
                    if local_instance:
 
                        for remote_session in remote_sessions.itervalues():
 
                        for remote_session in remote_sessions.values():
 
                            self.merge_instance(remote_session, local_instance)
 
                            remote_session.flush()
 

	
rattail/db/util.py
Show inline comments
 
@@ -114,7 +114,7 @@ def make_topo_sortkey(model, metadata=None):
 
        tables[table.name] = i
 

	
 
    log.debug("topo sortkeys for '{}' will be:\n{}".format(model.__name__, pprint.pformat(
 
        [(i, name) for name, i in sorted(tables.iteritems(), key=lambda t: t[1])])))
 
        [(i, name) for name, i in sorted(tables.items(), key=lambda t: t[1])])))
 

	
 
    def sortkey(name):
 
        mapper = orm.class_mapper(getattr(model, name))
rattail/exceptions.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -28,6 +28,8 @@ This module contains all core Rattail exception classes.
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
import six
 

	
 

	
 
class RattailError(Exception):
 
    """
 
@@ -41,6 +43,7 @@ class ConfigurationError(RattailError):
 
    """
 

	
 

	
 
@six.python_2_unicode_compatible
 
class SQLAlchemyNotInstalled(RattailError):
 
    """
 
    Error raised when an operation is requested which requires SQLAlchemy
 
@@ -49,24 +52,26 @@ class SQLAlchemyNotInstalled(RattailError):
 
    def __init__(self, error):
 
        self.error = error
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return ("Hm, looks like SQLAlchemy may not be installed?  "
 
                "(Perhaps you should do: 'pip install rattail[db]'?)  "
 
                "Original error was: {0}".format(self.error))
 
                "Original error was: {}".format(self.error))
 

	
 

	
 
@six.python_2_unicode_compatible
 
class WindowsExtensionsNotInstalled(RattailError):
 
    """
 
    Error raised when an operation is requested which requires the "Python for
 
    Windows Extensions" to be instealled, but it is not available.
 
    """
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return ("Cannot proceed because Python for Windows Extensions is not installed.  Please see "
 
                "https://rattailproject.org/moin/Installation/Windows/Python#Python_for_Windows_Extensions "
 
                "for more info.")
 

	
 

	
 
@six.python_2_unicode_compatible
 
class MailTemplateNotFound(ConfigurationError):
 

	
 
    def __init__(self, key):
 
@@ -78,26 +83,24 @@ class MailTemplateNotFound(ConfigurationError):
 
                "configured template folders.".format(self.key))
 

	
 

	
 
@six.python_2_unicode_compatible
 
class SenderNotFound(ConfigurationError):
 

	
 
    def __init__(self, key):
 
        self.key = key
 

	
 
    def __unicode__(self):
 
        return ("No email sender (From: address) found in config.  Please set '{0}.from' "
 
    def __str__(self):
 
        return ("No email sender (From: address) found in config.  Please set '{}.from' "
 
                "(or 'default.from') in the [rattail.mail] section.".format(self.key))
 

	
 

	
 
@six.python_2_unicode_compatible
 
class RecipientsNotFound(ConfigurationError):
 
    
 
    def __init__(self, key):
 
        self.key = key
 

	
 
    def __str__(self):
 
        return (b"No recipients found in config for '{0}' emails.  Please set '{0}.to' "
 
                "(or 'default.to') in the [rattail.mail] section.".format(self.key))
 

	
 
    def __unicode__(self):
 
        return ("No recipients found in config for '{0}' emails.  Please set '{0}.to' "
 
                "(or 'default.to') in the [rattail.mail] section.".format(self.key))
 

	
 
@@ -108,6 +111,7 @@ class FileOperationError(RattailError):
 
    """
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PathNotFound(FileOperationError):
 
    """
 
    Raised when "path not found" errors are encountered within the
 
@@ -119,8 +123,8 @@ class PathNotFound(FileOperationError):
 
    def __init__(self, original_error):
 
        self.original_error = original_error
 

	
 
    def __unicode__(self):
 
        return '{0}: {1}'.format(
 
    def __str__(self):
 
        return '{}: {}'.format(
 
            self.original_error.__class__.__name__,
 
            self.original_error)
 

	
 
@@ -136,28 +140,32 @@ class PalmError(RattailError):
 
    """
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PalmClassicDatabaseTypelibNotFound(PalmError):
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return ("The Python module for the Palm Classic Database type library "
 
                "could not be generated.  (Is the HotSync Manager software "
 
                "installed?)")
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PalmConduitManagerNotFound(PalmError):
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return ("The Palm Desktop Conduit Manager could not be instantiated.  "
 
                "(Is the HotSync Manager software installed?)")
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PalmConduitAlreadyRegistered(PalmError):
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return "The Rattail Palm conduit is already registered."
 

	
 

	
 
@six.python_2_unicode_compatible
 
class PalmConduitNotRegistered(PalmError):
 

	
 
    def __unicode__(self):
 
    def __str__(self):
 
        return "The Rattail Palm conduit is not registered."
rattail/fablib/core.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -30,6 +30,8 @@ import os
 
import re
 
import tempfile
 

	
 
import six
 

	
 
from fabric.api import sudo, run, cd, local, env, settings, put as fab_put
 
from fabric.contrib.files import exists, append, upload_template as fab_upload_template, is_link
 

	
 
@@ -60,7 +62,7 @@ def mkdir(paths, owner='root:root', mode=None):
 
    """
 
    Recursively make one or more directories.
 
    """
 
    if isinstance(paths, basestring):
 
    if isinstance(paths, six.string_types):
 
        paths = [paths]
 
    sudo('mkdir --parents {0}'.format(' '.join(paths)))
 
    if owner != 'root:root':
rattail/fablib/python.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -24,11 +24,12 @@
 
Fabric Library for Python
 
"""
 

	
 
from __future__ import unicode_literals
 
from __future__ import absolute_import
 
from __future__ import unicode_literals, absolute_import
 

	
 
from contextlib import contextmanager
 

	
 
import six
 

	
 
from fabric.api import sudo, run, prefix, cd, settings, env
 
from fabric.contrib.files import exists, append
 

	
 
@@ -129,7 +130,7 @@ def cdvirtualenv(name, subdirs=[], workon_home=None):
 
    Context manager to prefix your command(s) with the ``cdvirtualenv`` command.
 
    """
 
    workon_home = workon_home or getattr(env, 'python_workon_home', '/srv/envs')
 
    if isinstance(subdirs, basestring):
 
    if isinstance(subdirs, six.string_types):
 
        subdirs = [subdirs]
 
    path = '{0}/{1}'.format(workon_home, name)
 
    if subdirs:
rattail/filemon/linux.py
Show inline comments
 
@@ -97,7 +97,7 @@ class FileMonitorDaemon(Daemon):
 
                | pyinotify.IN_MOVED_TO)
 

	
 
        monitored = load_profiles(self.config)
 
        for key, profile in monitored.iteritems():
 
        for key, profile in monitored.items():
 

	
 
            # Create a file queue for the profile.
 
            profile.queue = Queue.Queue()
rattail/filemon/win32.py
Show inline comments
 
@@ -68,7 +68,7 @@ class RattailFileMonitor(Service):
 
            return False
 

	
 
        # Create monitor and action threads for each profile.
 
        for key, profile in self.monitored.iteritems():
 
        for key, profile in self.monitored.items():
 

	
 
            # Create a file queue for the profile.
 
            profile.queue = Queue.Queue()
rattail/gpc.py
Show inline comments
 
@@ -59,9 +59,9 @@ class GPC(object):
 
        to ``'upc'``.
 
        """
 

	
 
        value = str(value)
 
        value = six.text_type(value)
 
        if calc_check_digit is True or calc_check_digit == 'upc':
 
            value += str(barcodes.upc_check_digit(value))
 
            value += six.text_type(barcodes.upc_check_digit(value))
 
        self.value = int(value)
 

	
 
    def __eq__(self, other):
rattail/importing/handlers.py
Show inline comments
 
@@ -57,7 +57,7 @@ class ImportHandler(object):
 
        self.config = config
 
        self.enum = config.get_enum() if config else None
 
        self.importers = self.get_importers()
 
        for key, value in kwargs.iteritems():
 
        for key, value in kwargs.items():
 
            setattr(self, key, value)
 

	
 
    def get_importers(self):
 
@@ -74,7 +74,7 @@ class ImportHandler(object):
 
        """
 
        Returns the list of keys corresponding to the available importers.
 
        """
 
        return list(self.importers.iterkeys())
 
        return list(self.importers.keys())
 

	
 
    def get_default_keys(self):
 
        """
rattail/importing/importers.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -28,6 +28,8 @@ from __future__ import unicode_literals, absolute_import
 

	
 
import logging
 

	
 
import six
 

	
 
from rattail.db import cache
 
from rattail.db.util import QuerySequence
 
from rattail.time import make_utc
 
@@ -105,7 +107,7 @@ class Importer(object):
 
            for field in exclude_fields:
 
                if field in self.fields:
 
                    self.fields.remove(field)
 
        if isinstance(self.key, basestring):
 
        if isinstance(self.key, six.string_types):
 
            self.key = (self.key,)
 
        if self.key:
 
            for field in self.key:
 
@@ -134,7 +136,7 @@ class Importer(object):
 
        self.create = kwargs.pop('create', self.allow_create) and self.allow_create
 
        self.update = kwargs.pop('update', self.allow_update) and self.allow_update
 
        self.delete = kwargs.pop('delete', False) and self.allow_delete
 
        for key, value in kwargs.iteritems():
 
        for key, value in kwargs.items():
 
            setattr(self, key, value)
 

	
 
    def setup(self):
rattail/importing/postgresql.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -63,7 +63,7 @@ class BulkToPostgreSQL(BulkImporter, ToSQLAlchemy):
 

	
 
    def prep_data_for_postgres(self, data):
 
        data = dict(data)
 
        for key, value in data.iteritems():
 
        for key, value in data.items():
 
            data[key] = self.prep_value_for_postgres(value)
 
        return data
 

	
 
@@ -77,7 +77,7 @@ class BulkToPostgreSQL(BulkImporter, ToSQLAlchemy):
 

	
 
        if isinstance(value, datetime.datetime):
 
            value = make_utc(value, tzinfo=False)
 
        elif isinstance(value, basestring):
 
        elif isinstance(value, six.string_types):
 
            value = value.replace('\\', '\\\\')
 
            value = value.replace('\r', '\\r')
 
            value = value.replace('\n', '\\n')
rattail/palm.py
Show inline comments
 
@@ -145,7 +145,7 @@ def get_conduit_manager():
 

	
 
    try:
 
        conduit_mgr = Dispatch('PDStandard.PDSystemCondMgr')
 
    except com_error, error:
 
    except com_error as error:
 
        if error.hresult == CO_E_CLASSSTRING: # "Invalid class string"
 
            raise PalmConduitManagerNotFound()
 
        raise
 
@@ -253,7 +253,7 @@ def unregister_com_server():
 

	
 
    try:
 
        conduit = Dispatch('Rattail.PalmConduit')
 
    except com_error, error:
 
    except com_error as error:
 
        if error.hresult == CO_E_CLASSSTRING: # "Invalid class string"
 
            raise PalmConduitNotRegistered()
 
        raise
rattail/sil/batches.py
Show inline comments
 
@@ -26,7 +26,7 @@ Batch Stuff
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
import ConfigParser
 
from six.moves import configparser
 

	
 
import six
 
import lockfile
 
@@ -42,7 +42,7 @@ def consume_batch_id(source='RATAIL'):
 
    path = get_user_file('rattail.conf', createdir=True)
 
    with lockfile.LockFile(path):
 

	
 
        parser = ConfigParser.SafeConfigParser()
 
        parser = configparser.SafeConfigParser()
 
        parser.read(path)
 
        option = 'next_batch_id.{0}'.format(source)
 

	
rattail/sil/columns.py
Show inline comments
 
@@ -150,7 +150,7 @@ def get_column(name):
 
    if supported_columns is None:
 
        supported_columns = {}
 
        providers = load_entry_points('rattail.sil.column_providers')
 
        for provider in providers.itervalues():
 
        for provider in providers.values():
 
            supported_columns.update(provider())
 

	
 
    column = supported_columns.get(name)
rattail/sil/writer.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -24,14 +24,16 @@
 
SIL Writer
 
"""
 

	
 
from __future__ import unicode_literals
 
from __future__ import unicode_literals, absolute_import
 

	
 
import datetime
 
from decimal import Decimal
 

	
 
from .._version import __version__
 
from ..core import Object
 
from ..gpc import GPC
 
import six
 

	
 
from rattail import __version__
 
from rattail.core import Object
 
from rattail.gpc import GPC
 
from rattail.files import temp_path
 
from rattail.sil import batches
 

	
 
@@ -89,24 +91,23 @@ class Writer(Object):
 
        Otherwise, it is converted to a string if necessary, and quoted with
 
        apostrophes escaped.
 
        """
 

	
 
        if value is None:
 
            return ''
 
        if isinstance(value, GPC):
 
            return str(value)
 
            return six.text_type(value)
 
        if isinstance(value, int):
 
            return str(value)
 
            return six.text_type(value)
 
        if isinstance(value, float):
 
            return str(value)
 
            return six.text_type(value)
 
        if isinstance(value, Decimal):
 
            return str(value)
 
            return six.text_type(value)
 
        if isinstance(value, datetime.date):
 
            return value.strftime('%Y%j')
 
        if isinstance(value, datetime.time):
 
            return value.strftime('%H%M')
 
        if not isinstance(value, basestring):
 
            value = str(value)
 
        return "'%s'" % value.replace("'", "''")
 
        if not isinstance(value, six.string_types):
 
            value = six.text_type(value)
 
        return "'{}'".format(value.replace("'", "''"))
 

	
 
    def write(self, string):
 
        self.fileobj.write(string)
rattail/templates/mail/rattail_import_updates.html.mako
Show inline comments
 
@@ -63,7 +63,7 @@
 
      Please investigate at your convenience.
 
    </p>
 
    <ul>
 
      % for model, (created, updated, deleted) in changes.iteritems():
 
      % for model, (created, updated, deleted) in changes.items():
 
          <li>
 
            <a href="#${model}">${model}</a>
 
            - ${'{:,d}'.format(len(created))} created, ${'{:,d}'.format(len(updated))} updated, ${'{:,d}'.format(len(deleted))} deleted
 
@@ -76,7 +76,7 @@
 
    <p style="padding-left: 20px;">
 
      <code>${argv}</code>
 
    <p>
 
    % for model, (created, updated, deleted) in changes.iteritems():
 
    % for model, (created, updated, deleted) in changes.items():
 
        <h4>
 
          <a name="${model}">${model}</a>
 
          - ${'{:,d}'.format(len(created))} created, ${'{:,d}'.format(len(updated))} updated, ${'{:,d}'.format(len(deleted))} deleted
rattail/tests/importing/lib.py
Show inline comments
 
@@ -35,7 +35,7 @@ class ImporterTester(object):
 
    @contextmanager
 
    def host_data(self, data):
 
        self._host_data = data
 
        host_data = [self.importer.normalize_host_object(obj) for obj in data.itervalues()]
 
        host_data = [self.importer.normalize_host_object(obj) for obj in data.values()]
 
        with patch.object(self.importer, 'normalize_host_data') as normalize:
 
            normalize.return_value = host_data
 
            yield
 
@@ -44,7 +44,7 @@ class ImporterTester(object):
 
    def local_data(self, data):
 
        self._local_data = data
 
        local_data = {}
 
        for key, obj in data.iteritems():
 
        for key, obj in data.items():
 
            normal = self.importer.normalize_local_object(obj)
 
            local_data[self.importer.get_key(normal)] = {'object': obj, 'data': normal}
 
        with patch.object(self.importer, 'cache_local_data') as cache:
rattail/tests/importing/test_handlers.py
Show inline comments
 
@@ -559,7 +559,7 @@ class TestFromSQLAlchemyHandler(unittest.TestCase):
 
        session = object()
 
        handler = handlers.FromSQLAlchemyHandler(host_session=session)
 
        kwargs = handler.get_importer_kwargs(None)
 
        self.assertEqual(list(kwargs.iterkeys()), ['host_session'])
 
        self.assertEqual(list(kwargs.keys()), ['host_session'])
 
        self.assertIs(kwargs['host_session'], session)
 

	
 
    def test_begin_host_transaction(self):
 
@@ -596,7 +596,7 @@ class TestToSQLAlchemyHandler(unittest.TestCase):
 
        session = object()
 
        handler = handlers.ToSQLAlchemyHandler(session=session)
 
        kwargs = handler.get_importer_kwargs(None)
 
        self.assertEqual(list(kwargs.iterkeys()), ['session'])
 
        self.assertEqual(list(kwargs.keys()), ['session'])
 
        self.assertIs(kwargs['session'], session)
 

	
 
    def test_begin_local_transaction(self):
rattail/tests/importing/test_rattail_bulk.py
Show inline comments
 
@@ -42,7 +42,7 @@ class BulkImportTester(DualRattailTestCase, ImporterTester):
 
    def import_data(self, host_data=None, **kwargs):
 
        if host_data is None:
 
            fields = self.get_fields()
 
            host_data = list(self.copy_data().itervalues())
 
            host_data = list(self.copy_data().values())
 
            for data in host_data:
 
                for field in fields:
 
                    data.setdefault(field, None)
rattail/tests/importing/test_sqlalchemy.py
Show inline comments
 
@@ -53,7 +53,7 @@ class TestToSQLAlchemy(TestCase):
 
        self.session = Session()
 
        for data in WIDGETS:
 
            widget = Widget()
 
            for key, value in data.iteritems():
 
            for key, value in data.items():
 
                setattr(widget, key, value)
 
            self.session.add(widget)
 
        self.session.commit()
rattail/tests/test_config.py
Show inline comments
 
@@ -4,7 +4,7 @@ from __future__ import unicode_literals, absolute_import
 

	
 
import os
 
import unittest
 
import ConfigParser
 
from six.moves import configparser
 

	
 
from mock import patch
 
from fixture import TempIO
 
@@ -196,6 +196,6 @@ include = "{bogus}" "{app}" "{bogus}" "{site}"
 
        fileConfig.reset_mock()
 

	
 
        # invalid logging config is ignored
 
        fileConfig.side_effect = ConfigParser.NoSectionError('loggers')
 
        fileConfig.side_effect = configparser.NoSectionError('loggers')
 
        cfg.configure_logging()
 
        self.assertEqual(fileConfig.call_count, 1)
rattail/tests/test_core.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8; -*-
 

	
 
from __future__ import unicode_literals
 
from __future__ import unicode_literals, absolute_import
 

	
 
from unittest import TestCase
 

	
 
import six
 

	
 
from rattail import core
 

	
 

	
 
@@ -11,5 +13,5 @@ class TestCore(TestCase):
 

	
 
    def test_get_uuid(self):
 
        uuid = core.get_uuid()
 
        self.assertTrue(isinstance(uuid, str))
 
        self.assertTrue(isinstance(uuid, six.string_types))
 
        self.assertEqual(len(uuid), 32)
rattail/tests/test_exceptions.py
Show inline comments
 
@@ -16,12 +16,6 @@ class TestRecipientsNotFound(unittest.TestCase):
 
        exc = exceptions.RecipientsNotFound('testing')
 
        self.assertEqual(exc.key, 'testing')
 

	
 
    def test_str(self):
 
        exc = exceptions.RecipientsNotFound('testing')
 
        self.assertEqual(str(exc),
 
                         b"No recipients found in config for 'testing' emails.  Please set "
 
                         "'testing.to' (or 'default.to') in the [rattail.mail] section.")
 

	
 
    def test_unicode(self):
 
        exc = exceptions.RecipientsNotFound('testing')
 
        self.assertEqual(six.text_type(exc),
rattail/util.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2017 Lance Edgar
 
#  Copyright © 2010-2018 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -31,6 +31,7 @@ import datetime
 
import decimal
 
import subprocess
 

	
 
import six
 
from pkg_resources import iter_entry_points
 

	
 
try:
 
@@ -200,8 +201,8 @@ def pretty_quantity(value, empty_zero=False):
 
        value = int(value)
 
        if empty_zero and value == 0:
 
            return ''
 
        return str(value)
 
    return str(value).rstrip('0')
 
        return six.text_type(value)
 
    return six.text_type(value).rstrip('0')
 

	
 

	
 
def progress_loop(func, items, factory, *args, **kwargs):
rattail/win32/__init__.py
Show inline comments
 
@@ -60,7 +60,7 @@ def file_is_free(path):
 
            win32file.FILE_ATTRIBUTE_NORMAL,
 
            None)
 
        return True
 
    except error, e:
 
    except error as e:
 
        if e.winerror == ERROR_SHARING_VIOLATION:
 
            return False
 
        raise
 
@@ -87,7 +87,7 @@ def process_is_elevated():
 
    hToken = OpenProcessToken(hProcess, TOKEN_READ)
 
    try:
 
        elevated = GetTokenInformation(hToken, TokenElevation)
 
    except error, e:
 
    except error as e:
 
        if e.winerror == ERROR_INVALID_PARAMETER:
 
            return True # feign success if OS doesn't support this check
 
        raise
rattail/win32/registry.py
Show inline comments
 
@@ -51,7 +51,7 @@ def RegDeleteTree(key, subkey):
 
        while True:
 
            try:
 
                name, value, type_ = win32api.RegEnumValue(key, i)
 
            except pywintypes.error, e:
 
            except pywintypes.error as e:
 
                if e[0] == winerror.ERROR_NO_MORE_ITEMS:
 
                    break
 
            values.append(name)
 
@@ -62,7 +62,7 @@ def RegDeleteTree(key, subkey):
 
    orig_key = key
 
    try:
 
        key = win32api.RegOpenKeyEx(orig_key, subkey, 0, win32con.KEY_ALL_ACCESS)
 
    except pywintypes.error, e:
 
    except pywintypes.error as e:
 
        if e[0] != winerror.ERROR_FILE_NOT_FOUND:
 
            raise
 
    else:
 
@@ -70,6 +70,6 @@ def RegDeleteTree(key, subkey):
 
        win32api.RegCloseKey(key)
 
    try:
 
        win32api.RegDeleteKey(orig_key, subkey)
 
    except pywintypes.error, e:
 
    except pywintypes.error as e:
 
        if e[0] == winerror.ERROR_FILE_NOT_FOUND:
 
            pass
rattail/win32/service.py
Show inline comments
 
@@ -85,7 +85,7 @@ class Service(win32serviceutil.ServiceFramework):
 
                servicemanager.EVENTLOG_INFORMATION_TYPE,
 
                servicemanager.PYS_SERVICE_STARTED,
 
                (self._svc_name_, ''))
 
        except error, e:
 
        except error as e:
 
            if e.winerror == ERROR_LOG_FILE_FULL:
 
                log.error("SvcDoRun: Windows event log is full!")
 
            else:
 
@@ -116,7 +116,7 @@ class Service(win32serviceutil.ServiceFramework):
 
                servicemanager.EVENTLOG_INFORMATION_TYPE,
 
                servicemanager.PYS_SERVICE_STOPPED,
 
                (self._svc_name_, ''))
 
        except error, e:
 
        except error as e:
 
            if e.winerror == ERROR_LOG_FILE_FULL:
 
                log.error("SvcDoRun: Windows event log is full!")
 
            else:
rattail/win32/users.py
Show inline comments
 
@@ -50,7 +50,7 @@ def user_exists(username, server=None):
 

	
 
    try:
 
        info = win32net.NetUserGetInfo(server, username, 0)
 
    except error, e:
 
    except error as e:
 
        if e.winerror == NERR_UserNotFound:
 
            return False
 
        raise
0 comments (0 inline, 0 general)