From 07690f9c945f365f2dc3f5a34be974332d30542c Mon Sep 17 00:00:00 2001 From: gryf Date: Sun, 24 Feb 2019 13:16:23 +0100 Subject: [PATCH] Added basic Python3 support --- README.rst | 13 ++++++------- pygtktalog/__init__.py | 18 +++++++----------- pygtktalog/dbobjects.py | 3 +-- pygtktalog/logger.py | 2 +- pygtktalog/misc.py | 4 ++-- pygtktalog/scan.py | 14 ++++---------- pygtktalog/video.py | 21 +++++++++++---------- scripts/cmdcatalog.py | 30 +++++++++++++++--------------- setup.py | 5 +++-- 9 files changed, 50 insertions(+), 60 deletions(-) diff --git a/README.rst b/README.rst index a89852c..6048835 100644 --- a/README.rst +++ b/README.rst @@ -44,15 +44,14 @@ Requirements pyGTKtalog requires python and following libraries: -* `python 2.7`_ -* `sqlalchemy 1.0`_ -* `pygtk 2.24`_ (only for ``gtktalog.py``) +* `python 3`_, tested on python 3.6 +* `sqlalchemy 1.2`_ or higher +* `pygtk 2.24`_ (only for ``gtktalog.py``, will not work with python3) * `pillow`_ for image manipulation * `exifread`_ for parse EXIF information It may work on other (lower) version of libraries, and it should work with -higher versions of libraries, although it will not work on Python 3 yet, nor -GTK3. +higher versions of libraries. GTK3 support will follow. pyGTKtalog extensively uses external programs in unix spirit, however there is small possibility of using it Windows (probably with limitations) and quite big @@ -148,7 +147,7 @@ file in top-level directory. .. _paver: https://pythonhosted.org/paver/ .. _pillow: https://python-pillow.org/ .. _pygtk 2.24: http://www.pygtk.org -.. _python 2.7: http://www.python.org/ -.. _sqlalchemy 1.0: http://www.sqlalchemy.org +.. _python 3: http://www.python.org/ +.. _sqlalchemy 1.2: http://www.sqlalchemy.org .. _tagging files: http://en.wikipedia.org/wiki/tag_%28metadata%29 .. _tox: https://testrun.org/tox diff --git a/pygtktalog/__init__.py b/pygtktalog/__init__.py index 6481e9f..55e8c54 100644 --- a/pygtktalog/__init__.py +++ b/pygtktalog/__init__.py @@ -6,19 +6,15 @@ Created: 2009-05-05 """ -__version__ = "2.0.0" +__version__ = "3.0.0" __appname__ = "pyGTKtalog" -__copyright__ = u"\u00A9 Roman 'gryf' Dobosz" +__copyright__ = "\u00A9 Roman 'gryf' Dobosz" __summary__ = "%s is simple tool for managing file collections." % __appname__ __web__ = "http://github.com/gryf/pygtktalog" import os -import sys import locale import gettext -import __builtin__ - -from pygtktalog.logger import get_logger __all__ = ['dbcommon', @@ -49,10 +45,10 @@ except locale.Error: # module.textdomain(GETTEXT_DOMAIN) # register the gettext function for the whole interpreter as "_" -__builtin__._ = gettext.gettext +_ = gettext.gettext -# wrap errors into usefull message -#def log_exception(exc_type, exc_val, traceback): -# get_logger(__name__).error(exc_val) +# # wrap errors into usefull message +# def log_exception(exc_type, exc_val, traceback): +# get_logger(__name__).error(exc_val) # -#sys.excepthook = log_exception +# sys.excepthook = log_exception diff --git a/pygtktalog/dbobjects.py b/pygtktalog/dbobjects.py index 0fc09f4..48d71c3 100644 --- a/pygtktalog/dbobjects.py +++ b/pygtktalog/dbobjects.py @@ -63,8 +63,7 @@ class File(Base): self.source = src def __repr__(self): - return "" % (self.filename.encode('utf-8'), - str(self.id)) + return "" % (self.filename, str(self.id)) def get_all_children(self): """ diff --git a/pygtktalog/logger.py b/pygtktalog/logger.py index 36fb674..f1d5256 100644 --- a/pygtktalog/logger.py +++ b/pygtktalog/logger.py @@ -36,7 +36,7 @@ def cprint(txt, color): "magenta": MAGENTA, "cyan": CYAN, "white": WHITE} - print COLOR_SEQ % (30 + color_map[color]) + txt + RESET_SEQ + print(COLOR_SEQ % (30 + color_map[color]) + txt + RESET_SEQ) class DummyFormater(logging.Formatter): diff --git a/pygtktalog/misc.py b/pygtktalog/misc.py index f9fa37f..35ceb4a 100644 --- a/pygtktalog/misc.py +++ b/pygtktalog/misc.py @@ -50,7 +50,7 @@ def calculate_image_path(dbpath=None, create=False): if not os.path.exists(images_dir): try: os.mkdir(images_dir) - except OSError, err: + except OSError as err: if err.errno != errno.EEXIST: raise elif not os.path.exists(images_dir): @@ -60,7 +60,7 @@ def calculate_image_path(dbpath=None, create=False): def mk_paths(fname, img_path): """Make path for provided pathname by calculating crc32 out of file""" - with open(fname) as fobj: + with open(fname, 'r+b') as fobj: new_path = "%x" % (crc32(fobj.read(10*1024*1024)) & 0xffffffff) new_path = [new_path[i:i + 2] for i in range(0, len(new_path), 2)] diff --git a/pygtktalog/scan.py b/pygtktalog/scan.py index 350f04d..4c2a754 100644 --- a/pygtktalog/scan.py +++ b/pygtktalog/scan.py @@ -155,7 +155,7 @@ class Scan(object): # in case of such, better get me a byte string. It is not perfect # though, since it WILL crash if the update_path would contain some # unconvertable characters. - update_path = update_path.encode("utf-8") + update_path = update_path # refresh objects LOG.debug("Refreshing objects") @@ -199,8 +199,7 @@ class Scan(object): '.ogm': 'video', '.ogv': 'video'} - fp = os.path.join(fobj.filepath.encode(sys.getfilesystemencoding()), - fobj.filename.encode(sys.getfilesystemencoding())) + fp = os.path.join(fobj.filepath, fobj.filename) mimeinfo = mimetypes.guess_type(fp) if mimeinfo[0]: @@ -287,13 +286,8 @@ class Scan(object): """ fullpath = os.path.join(path, fname) - fname = fname.decode(sys.getfilesystemencoding(), - errors="replace") - path = path.decode(sys.getfilesystemencoding(), - errors="replace") - if ftype == TYPE['link']: - fname = fname + " -> " + os.readlink(fullpath).decode('utf-8') + fname = fname + " -> " + os.readlink(fullpath) fob = {'filename': fname, 'path': path, @@ -378,7 +372,7 @@ class Scan(object): LOG.info("Scanning `%s' [%s/%s]", fullpath, self.current_count, self.files_count) - root, dirs, files = os.walk(fullpath).next() + root, dirs, files = next(os.walk(fullpath)) for fname in files: fpath = os.path.join(root, fname) extension = os.path.splitext(fname)[1] diff --git a/pygtktalog/video.py b/pygtktalog/video.py index 65b7b8a..0228c03 100644 --- a/pygtktalog/video.py +++ b/pygtktalog/video.py @@ -92,10 +92,10 @@ class Video(object): if scale < 1: return None - no_pictures = self.tags['length'] / scale + no_pictures = self.tags['length'] // scale if no_pictures > 8: - no_pictures = (no_pictures / 8) * 8 # only multiple of 8, please. + no_pictures = (no_pictures // 8) * 8 # only multiple of 8, please. else: # for really short movies no_pictures = 4 @@ -113,16 +113,16 @@ class Video(object): """ Return formatted tags as a string """ - out_tags = u'' + out_tags = '' if 'container' in self.tags: - out_tags += u"Container: %s\n" % self.tags['container'] + out_tags += "Container: %s\n" % self.tags['container'] if 'width' in self.tags and 'height' in self.tags: - out_tags += u"Resolution: %sx%s\n" % (self.tags['width'], - self.tags['height']) + out_tags += "Resolution: %sx%s\n" % (self.tags['width'], + self.tags['height']) if 'duration' in self.tags: - out_tags += u"Duration: %s\n" % self.tags['duration'] + out_tags += "Duration: %s\n" % self.tags['duration'] if 'video_codec' in self.tags: out_tags += "Video codec: %s\n" % self.tags['video_codec'] @@ -178,7 +178,7 @@ class Video(object): @directory - full output directory name @no_pictures - number of pictures to take """ - step = float(self.tags['length'] / (no_pictures + 1)) + step = self.tags['length'] / (no_pictures + 1) current_time = 0 for dummy in range(1, no_pictures + 1): current_time += step @@ -191,7 +191,8 @@ class Video(object): try: shutil.move(os.path.join(directory, "00000001.jpg"), os.path.join(directory, "picture_%s.jpg" % time)) - except IOError, (errno, strerror): + except IOError as exc: + errno, strerror = exc.args LOG.error('error capturing file from movie "%s" at position ' '%s. Errors: %s, %s', self.filename, time, errno, strerror) @@ -234,7 +235,7 @@ class Video(object): img_list = [Image.open(os.path.join(directory, fn)).resize(dim) \ for fn in ifn_list] - rows = no_pictures / row_length + rows = no_pictures // row_length cols = row_length isize = (cols * dim[0] + cols + 1, rows * dim[1] + rows + 1) diff --git a/scripts/cmdcatalog.py b/scripts/cmdcatalog.py index 5a33d70..80cc51a 100755 --- a/scripts/cmdcatalog.py +++ b/scripts/cmdcatalog.py @@ -40,7 +40,7 @@ def colorize(txt, color): def asserdb(func): def wrapper(args): if not os.path.exists(args.db): - print colorize("File `%s' does not exists!" % args.db, 'red') + print(colorize("File `%s' does not exists!" % args.db, 'red')) sys.exit(1) func(args) return wrapper @@ -151,7 +151,7 @@ class Iface(object): node = self.root msg = "Content of path `/':" - print colorize(msg, 'white') + print(colorize(msg, 'white')) if recursive: items = self._walk(node) @@ -168,7 +168,7 @@ class Iface(object): else: filenames = sorted(items.keys()) - print '\n'.join(filenames) + print('\n'.join(filenames)) def update(self, path, dir_to_update=None): """ @@ -179,8 +179,8 @@ class Iface(object): self.root = self.root.filter(dbo.File.type == dbo.TYPE['root']).first() node = self._resolve_path(path) if node == self.root: - print colorize('Cannot update entire db, since root was provided ' - 'as path.', 'red') + print(colorize('Cannot update entire db, since root was provided ' + 'as path.', 'red')) return if not dir_to_update: @@ -189,8 +189,8 @@ class Iface(object): if not os.path.exists(dir_to_update): raise OSError("Path to updtate doesn't exists: %s", dir_to_update) - print colorize("Updating node `%s' against directory " - "`%s'" % (path, dir_to_update), 'white') + print(colorize("Updating node `%s' against directory " + "`%s'" % (path, dir_to_update), 'white')) if not self.dry_run: scanob = scan.Scan(dir_to_update) # scanob.update_files(node.id) @@ -215,8 +215,8 @@ class Iface(object): self.sess.add(config) self.sess.commit() - print colorize("Creating new db against directory `%s'" % dir_to_add, - 'white') + print(colorize("Creating new db against directory `%s'" % dir_to_add, + 'white')) if not self.dry_run: if data_dir == ':same_as_db:': misc.calculate_image_path(None, True) @@ -234,7 +234,7 @@ class Iface(object): if not os.path.exists(dir_to_add): raise OSError("Path to add doesn't exists: %s", dir_to_add) - print colorize("Adding directory `%s'" % dir_to_add, 'white') + print(colorize("Adding directory `%s'" % dir_to_add, 'white')) if not self.dry_run: scanob = scan.Scan(dir_to_add) scanob.add_files() @@ -273,19 +273,19 @@ class Iface(object): result = [] for word in search_words: - phrase = u'%%%s%%' % word.decode('utf-8') + phrase = u'%%%s%%' % word query = query.filter(dbo.File.filename.like(phrase)) for item in query.all(): result.append(self._get_full_path(item)) if not result: - print "No results for `%s'" % ' '.join(search_words) + print("No results for `%s'" % ' '.join(search_words)) return result.sort() for item in result: - print self._annotate(item, search_words) + print(self._annotate(item, search_words)) def fsck(self): """Fsck orphaned images/thumbs""" @@ -342,9 +342,9 @@ class Iface(object): sys.stdout.flush() if self.dry_run: - print "Following files are not associated to any items in the DB:" + print("Following files are not associated to any items in the DB:") for filename in sorted(files_to_remove): - print filename + print(filename) self.sess.rollback() else: _remove_files(image_path, files_to_remove) diff --git a/setup.py b/setup.py index f0d47e7..2447dd6 100755 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ from distutils.core import setup setup(name='pygtktalog', packages=['pygtktalog'], - version='2.0', + version='3.0', description='Catalog application with GTK interface', author='Roman Dobosz', author_email='gryf73@gmail.com', @@ -18,7 +18,8 @@ setup(name='pygtktalog', scripts=['scripts/cmdcatalog.py'], classifiers=['Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 2 :: Only', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: End Users/Desktop',