diff --git a/ice/__main__.py b/ice/__main__.py index 358401d..c11a8fd 100755 --- a/ice/__main__.py +++ b/ice/__main__.py @@ -4,16 +4,21 @@ import traceback try: - from runners import command_line_runner + from .runners import command_line_runner if __name__ == "__main__": runner = command_line_runner.CommandLineRunner() runner.run(sys.argv) # Keeps the console from closing (until the user hits enter) so they can # read any console output - print "" - print "Close the window, or hit enter to exit..." - raw_input() + print("") + print("Close the window, or hit enter to exit...") + + # Wait for the user to press the enter key. Python2 and Python3 compatibilty. + try: + raw_input() + except: + input() except Exception as e: stderr = sys.stderr with open('error.log', 'w') as f: @@ -21,7 +26,12 @@ traceback.print_exc() sys.stderr = stderr traceback.print_exc() - print "" - print "An error has occurred! A copy of the crash report has been saved to 'error.log'." - print "If this continues please submit an issue on our Github page (http://github.com/scottrice/Ice)" - raw_input() + print("") + print("An error has occurred! A copy of the crash report has been saved to 'error.log'.") + print("If this continues please submit an issue on our Github page (http://github.com/scottrice/Ice)") + + # Wait for the user to press the enter key. Python2 and Python3 compatibilty. + try: + raw_input() + except: + input() diff --git a/ice/backups.py b/ice/backups.py index a6dac13..9ef5675 100644 --- a/ice/backups.py +++ b/ice/backups.py @@ -5,9 +5,9 @@ from pysteam import shortcuts -import paths +from . import paths -from logs import logger +from .logs import logger def default_backups_directory(): return os.path.join(paths.application_data_directory(), 'Backups') diff --git a/ice/configuration.py b/ice/configuration.py index a844310..9bf16d4 100644 --- a/ice/configuration.py +++ b/ice/configuration.py @@ -11,8 +11,8 @@ import collections import os -import model -import paths +from . import model +from . import paths ConfigOption = collections.namedtuple('ConfigOption', [ 'identifier', diff --git a/ice/consoles.py b/ice/consoles.py index 6ae26b0..4981900 100644 --- a/ice/consoles.py +++ b/ice/consoles.py @@ -2,7 +2,7 @@ import os -import roms +from . import roms def console_roms_directory(configuration, console): """ diff --git a/ice/debug.py b/ice/debug.py index f773199..29e1c7a 100644 --- a/ice/debug.py +++ b/ice/debug.py @@ -4,9 +4,9 @@ import pastebin -import filesystem -import paths -import settings +from . import filesystem +from . import paths +from . import settings def debug_log_contents(): fs = filesystem.RealFilesystem() @@ -41,6 +41,6 @@ def make_paste(contents): def paste_debug_logs(): url = make_paste(debug_log_contents()) - print "You can find your logs at:\n" - print "\t%s\n" % url - print "Please include this link in any bug reports." + print("You can find your logs at:\n") + print("\t%s\n" % url) + print("Please include this link in any bug reports.") diff --git a/ice/decorators.py b/ice/decorators.py index a658e54..597aa02 100644 --- a/ice/decorators.py +++ b/ice/decorators.py @@ -1,6 +1,6 @@ # encoding: utf-8 -from logs import logger +from .logs import logger def catch_exceptions(msg): def decorator(func): diff --git a/ice/environment_checker.py b/ice/environment_checker.py index 5e36aea..9300d2a 100644 --- a/ice/environment_checker.py +++ b/ice/environment_checker.py @@ -7,9 +7,9 @@ import psutil import sys -from error.path_existance_error import PathExistanceError -from error.process_running_error import ProcessRunningError -from error.writable_path_error import WritablePathError +from .error.path_existance_error import PathExistanceError +from .error.process_running_error import ProcessRunningError +from .error.writable_path_error import WritablePathError class EnvironmentChecker(object): diff --git a/ice/error/path_existance_error.py b/ice/error/path_existance_error.py index 98bd39a..81c83e5 100644 --- a/ice/error/path_existance_error.py +++ b/ice/error/path_existance_error.py @@ -1,6 +1,6 @@ import os -from env_checker_error import EnvCheckerError +from .env_checker_error import EnvCheckerError class PathExistanceError(Exception): diff --git a/ice/error/process_running_error.py b/ice/error/process_running_error.py index f7a210f..310c2e4 100644 --- a/ice/error/process_running_error.py +++ b/ice/error/process_running_error.py @@ -1,4 +1,4 @@ -from env_checker_error import EnvCheckerError +from .env_checker_error import EnvCheckerError class ProcessRunningError(Exception): diff --git a/ice/error/writable_path_error.py b/ice/error/writable_path_error.py index 742be30..c5a21b2 100644 --- a/ice/error/writable_path_error.py +++ b/ice/error/writable_path_error.py @@ -1,4 +1,4 @@ -from env_checker_error import EnvCheckerError +from .env_checker_error import EnvCheckerError class WritablePathError(Exception): diff --git a/ice/gridproviders/combined_provider.py b/ice/gridproviders/combined_provider.py index aec91fe..f28b4d4 100755 --- a/ice/gridproviders/combined_provider.py +++ b/ice/gridproviders/combined_provider.py @@ -2,7 +2,7 @@ from functools import reduce -import grid_image_provider +from . import grid_image_provider from ice.logs import logger diff --git a/ice/gridproviders/consolegrid_provider.py b/ice/gridproviders/consolegrid_provider.py index 127d89f..f328d91 100755 --- a/ice/gridproviders/consolegrid_provider.py +++ b/ice/gridproviders/consolegrid_provider.py @@ -9,10 +9,17 @@ import sys import os -import urllib -import urllib2 -import grid_image_provider +# Python 2 and 3 compatibility for urllib. +try: + from urllib.request import urlopen, Request, urlretrieve + from urllib.error import HTTPError, URLError + from urllib.parse import quote +except ImportError: + from urllib import urlencode, quote, urlretrieve + from urllib2 import urlopen, Request, HTTPError, URLError + +from . import grid_image_provider from ice.logs import logger @@ -29,7 +36,7 @@ def is_enabled(): def consolegrid_top_picture_url(self, rom): host = self.api_url() - quoted_name = urllib.quote(rom.name) + quoted_name = quote(rom.name) return "%s?console=%s&game=%s" % (host, rom.console.shortname, quoted_name) def find_url_for_rom(self, rom): @@ -38,7 +45,7 @@ def find_url_for_rom(self, rom): ConsoleGrid.com """ try: - response = urllib2.urlopen(self.consolegrid_top_picture_url(rom)) + response = urlopen(self.consolegrid_top_picture_url(rom)) if response.getcode() == 204: name = rom.name console = rom.console.fullname @@ -47,7 +54,7 @@ def find_url_for_rom(self, rom): ) else: return response.read() - except urllib2.URLError as error: + except URLError as error: # Connection was refused. ConsoleGrid may be down, or something bad # may have happened logger.debug( @@ -59,7 +66,7 @@ def download_image(self, url): Downloads the image at 'url' and returns the path to the image on the local filesystem """ - (path, headers) = urllib.urlretrieve(url) + (path, headers) = urlretrieve(url) return path def image_for_rom(self, rom): diff --git a/ice/gridproviders/local_provider.py b/ice/gridproviders/local_provider.py index 0c43246..066dc9f 100755 --- a/ice/gridproviders/local_provider.py +++ b/ice/gridproviders/local_provider.py @@ -10,7 +10,7 @@ import sys import os -import grid_image_provider +from . import grid_image_provider from ice.logs import logger diff --git a/ice/logs.py b/ice/logs.py index 04c6de8..33bd38f 100644 --- a/ice/logs.py +++ b/ice/logs.py @@ -15,7 +15,7 @@ import logging import logging.handlers -import paths +from . import paths def is_test_stack_frame(frame): # The path of the executing file is in frame[1] diff --git a/ice/parsing/rom_parser.py b/ice/parsing/rom_parser.py index d364596..db4f972 100644 --- a/ice/parsing/rom_parser.py +++ b/ice/parsing/rom_parser.py @@ -11,7 +11,7 @@ class ROMParser(object): # Regex that matches the entire string up until it hits the first '[', # ']', '(', ')', or '.' # DOESN'T WORK FOR GAMES WITH ()s IN THEIR NAME - ur"(?P[^\(\)\[\]\.]*).*", + u"(?P[^\(\)\[\]\.]*).*", ] def __init__(self): diff --git a/ice/persistence/config_file_backing_store.py b/ice/persistence/config_file_backing_store.py index acf0549..5696608 100644 --- a/ice/persistence/config_file_backing_store.py +++ b/ice/persistence/config_file_backing_store.py @@ -6,16 +6,20 @@ Copyright (c) 2014 Scott Rice. All rights reserved. """ -import ConfigParser +# Python 2 and 3 compatibility for ConfigParser +try: + from configparser import ConfigParser, RawConfigParser, NoSectionError, NoOptionError, DuplicateSectionError +except ImportError: + from ConfigParser import ConfigParser, RawConfigParser, NoSectionError, NoOptionError, DuplicateSectionError -import backing_store +from . import backing_store class ConfigFileBackingStore(backing_store.BackingStore): def __init__(self, path): super(ConfigFileBackingStore, self).__init__(path) - self.configParser = ConfigParser.RawConfigParser() + self.configParser = RawConfigParser() self.configParser.read(self.path) def identifiers(self): @@ -24,7 +28,7 @@ def identifiers(self): def add_identifier(self, ident): try: self.configParser.add_section(ident) - except ConfigParser.DuplicateSectionError: + except DuplicateSectionError: raise ValueError("The identifier `%s` already exists" % str(ident)) def remove_identifier(self, ident): @@ -33,14 +37,14 @@ def remove_identifier(self, ident): def keys(self, ident): try: return self.configParser.options(ident) - except ConfigParser.NoSectionError: + except NoSectionError: raise ValueError("No identifier named `%s` exists" % str(ident)) def get(self, ident, key, default=None): try: val = self.configParser.get(ident, key.lower()) return val - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + except (NoSectionError, NoOptionError): return default def set(self, ident, key, value): diff --git a/ice/rom_finder.py b/ice/rom_finder.py index 37bf9b8..50d9623 100644 --- a/ice/rom_finder.py +++ b/ice/rom_finder.py @@ -1,10 +1,10 @@ - +import sys from functools import partial -import consoles -import model +from . import consoles +from . import model -from logs import logger +from .logs import logger class ROMFinder(object): @@ -47,6 +47,11 @@ def roms_for_consoles(self, consoles): Equivalent to calling `roms_for_console` on every element of `consoles` and combining the results """ - # Abuses the fact that the `+` operator is overloaded with lists to turn - # our list of lists into a single giant list. Yay for duck typing? - return sum(map(self.roms_for_console, consoles), []) + # Python 2 and 3 act differently on map() types, since map() in Python3 now returns an iterator + if sys.version_info[0] >= 3: + # TODO: Fix this somehow? + return list(map(self.roms_for_console, consoles)) + else: + # Abuses the fact that the `+` operator is overloaded with lists to turn + # our list of lists into a single giant list. Yay for duck typing? + return sum(map(self.roms_for_console, consoles), []) diff --git a/ice/roms.py b/ice/roms.py index 0ea9b97..1bd963c 100644 --- a/ice/roms.py +++ b/ice/roms.py @@ -15,8 +15,8 @@ from pysteam import model -import emulators -import paths +from . import emulators +from . import paths # LEGACY: At one point I added this to every shortcut that Ice made. That was # a terrible idea, and I'm keeping this definition here just in case I ever diff --git a/ice/runners/command_line_runner.py b/ice/runners/command_line_runner.py index 3b8b1bb..c4a8c28 100644 --- a/ice/runners/command_line_runner.py +++ b/ice/runners/command_line_runner.py @@ -9,7 +9,7 @@ from pysteam.steam import get_steam -from ice_engine import IceEngine +from .ice_engine import IceEngine from ice import debug from ice.filesystem import RealFilesystem diff --git a/ice/settings.py b/ice/settings.py index f3e6745..2c52ba6 100644 --- a/ice/settings.py +++ b/ice/settings.py @@ -1,18 +1,19 @@ # encoding: utf-8 import os +import sys -import configuration -import paths +from . import configuration +from . import paths -from logs import logger -from gridproviders.combined_provider import CombinedProvider -from gridproviders.consolegrid_provider import ConsoleGridProvider -from gridproviders.local_provider import LocalProvider -from persistence.backed_object_manager import BackedObjectManager -from persistence.config_file_backing_store import ConfigFileBackingStore -from persistence.adapters.console_adapter import ConsoleBackedObjectAdapter -from persistence.adapters.emulator_adapter import EmulatorBackedObjectAdapter +from .logs import logger +from .gridproviders.combined_provider import CombinedProvider +from .gridproviders.consolegrid_provider import ConsoleGridProvider +from .gridproviders.local_provider import LocalProvider +from .persistence.backed_object_manager import BackedObjectManager +from .persistence.config_file_backing_store import ConfigFileBackingStore +from .persistence.adapters.console_adapter import ConsoleBackedObjectAdapter +from .persistence.adapters.emulator_adapter import EmulatorBackedObjectAdapter def find_settings_file(name, filesystem): """ @@ -69,8 +70,15 @@ def image_provider(config): names = map(normalize, config.provider_spec.split(",")) instances = map(lambda name: providerByName[name](), names) logger.debug("Creating with component providers: %s" % str(instances)) - if len(instances) == 0: - logger.error("No image providers specified. Ice will run, but will not \ - find grid images for your ROMs. If this wasnt intentional, \ - see config.txt.") + # map() returns an interator in Python 3, so we have to check empty in a different way. + if sys.version_info[0] >= 3: + if not instances: + logger.error("No image providers specified. Ice will run, but will not \ + find grid images for your ROMs. If this wasnt intentional, \ + see config.txt.") + else: + if len(instances) == 0: + logger.error("No image providers specified. Ice will run, but will not \ + find grid images for your ROMs. If this wasnt intentional, \ + see config.txt.") return CombinedProvider(*instances) diff --git a/ice/steam_grid_updater.py b/ice/steam_grid_updater.py index 48a8c96..79805a1 100644 --- a/ice/steam_grid_updater.py +++ b/ice/steam_grid_updater.py @@ -2,9 +2,9 @@ from pysteam import grid from pysteam import shortcuts -import roms +from . import roms -from logs import logger +from .logs import logger class SteamGridUpdater(object): diff --git a/ice/steam_shortcut_synchronizer.py b/ice/steam_shortcut_synchronizer.py index 492aa46..97fea75 100644 --- a/ice/steam_shortcut_synchronizer.py +++ b/ice/steam_shortcut_synchronizer.py @@ -1,10 +1,10 @@ from pysteam import shortcuts -import roms +from . import roms -from consoles import console_roms_directory -from logs import logger +from .consoles import console_roms_directory +from .logs import logger class SteamShortcutSynchronizer(object): diff --git a/tools/install-git-hooks.py b/tools/install-git-hooks.py index 877cae3..37b374c 100755 --- a/tools/install-git-hooks.py +++ b/tools/install-git-hooks.py @@ -29,7 +29,7 @@ class WindowsSymlinkAdapter(object): def __init__(self): - print WINDOWS_VERSION_NEEDS_TESTING_ERROR_MESSAGE + print(WINDOWS_VERSION_NEEDS_TESTING_ERROR_MESSAGE) sys.exit(1) # Real attempted implementation import win32file @@ -69,12 +69,12 @@ def remove_preexisting_hooks_dir(hooks_directory): return False hooks = os.listdir(hooks_directory) if len(hooks) == 0: - print "Removing empty hooks directory" + print("Removing empty hooks directory") shutil.rmtree(hooks_directory) return False samples = glob.glob(os.path.join(hooks_directory, "*.sample")) if len(hooks) == len(samples): - print "Removing sample hooks directory" + print("Removing sample hooks directory") shutil.rmtree(hooks_directory) return False return True @@ -91,10 +91,10 @@ def create_hooks_symlink(adapter, target_hooks_path): # This script has already been run, we're done sys.exit(0) - print "Installing symlink into %s" % install_path + print("Installing symlink into %s" % install_path) if remove_preexisting_hooks_dir(install_path): - print GIT_HOOKS_ALREADY_EXIST_ERROR_MESSAGE + print(GIT_HOOKS_ALREADY_EXIST_ERROR_MESSAGE) sys.exit(1) adapter.create(install_path, target_hooks_path)