1
0
mirror of https://github.com/gryf/fs-uae-wrapper.git synced 2026-03-26 06:33:35 +01:00

5 Commits

Author SHA1 Message Date
148d28dac2 Make archive name and archiver for savestate optional. 2024-09-11 21:23:49 +02:00
60139d1728 Move to pyproject. 2024-09-11 19:53:52 +02:00
68b1f2a787 Correct homepage in setup.cfg 2022-10-02 14:57:38 +02:00
75d2cff96c Drop Python2 support. 2022-09-02 19:01:56 +02:00
114984cbee Go with setup.cfg instead of setup.py. 2022-09-02 18:47:17 +02:00
13 changed files with 102 additions and 104 deletions

View File

@@ -25,7 +25,7 @@ preferably in a archive file vs a bunch of files.
Requirements Requirements
============ ============
- Python (2 or 3) - Python 3
- `fs-uae`_ (obviously :) - `fs-uae`_ (obviously :)
Fs-uae-wrapper supports several types of archives: Fs-uae-wrapper supports several types of archives:
@@ -157,8 +157,8 @@ Options used:
* ``wrapper`` (required) with ``cd32`` as an value * ``wrapper`` (required) with ``cd32`` as an value
* ``wrapper_archive`` (required) path to the archive with CD32 iso/cue/wav * ``wrapper_archive`` (required) path to the archive with CD32 iso/cue/wav
* ``wrapper_archiver`` (conditionally required) archiver to use for storage * ``wrapper_archiver`` (optional) archiver to use for storage save state -
save state default ``7z``.
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical * ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files message during extracting files
* ``wrapper_save_state`` (optional) if set to "1", will load/archive save state * ``wrapper_save_state`` (optional) if set to "1", will load/archive save state
@@ -175,7 +175,7 @@ fragment of configuration file is saved as ``ChaosEngine.fs-uae``:
[config] [config]
wrapper = cd32 wrapper = cd32
wrapper_archive = ChaosEngine.7z wrapper_archive = ChaosEngine.7z
wrapper_archiver = 7z wrapper_archiver = zip
wrapper_gui_msg = 1 wrapper_gui_msg = 1
amiga_model = CD32 amiga_model = CD32
@@ -219,10 +219,12 @@ archive
Options used: Options used:
* ``wrapper`` (required) with ``archive`` as an value * ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archive`` (required) path to the archive with assets (usually means * ``wrapper_archive`` (optional) path to the archive with assets (usually means
whole system directories, floppies or hard disk images) whole system directories, floppies or hard disk images), defaults to same
* ``wrapper_archiver`` (conditionally required) archiver to use for storage name as configuration file with some detected archive extension. Note, that
save state name is case sensitive
* ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
* ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical * ``wrapper_gui_msg`` (optional) if set to "1", will display a graphical
message during extracting files message during extracting files
* ``wrapper_persist_data`` (optional) if set to "1", will compress (possibly * ``wrapper_persist_data`` (optional) if set to "1", will compress (possibly
@@ -277,7 +279,8 @@ savestate
Options used: Options used:
* ``wrapper`` (required) with ``archive`` as an value * ``wrapper`` (required) with ``archive`` as an value
* ``wrapper_archiver`` (required) archiver to use for storage save state * ``wrapper_archiver`` (optional) archiver to use for storage save state -
default ``7z``.
This module is primarily used to run emulator with read only media attached This module is primarily used to run emulator with read only media attached
(like images of floppies or uncompressed CD-ROMs) and its purpose is to (like images of floppies or uncompressed CD-ROMs) and its purpose is to

View File

@@ -237,9 +237,9 @@ class Base(object):
return True return True
if 'wrapper_archiver' not in self.all_options: if 'wrapper_archiver' not in self.all_options:
logging.error("Configuration lacks of required " logging.warning("Configuration lacks of optional "
"`wrapper_archiver' option.") "`wrapper_archiver' option, fall back to 7z")
return False self.all_options['wrapper_archiver'] = "7z"
if not path.which(self.all_options['wrapper_archiver']): if not path.which(self.all_options['wrapper_archiver']):
logging.error("Cannot find archiver `%s'.", logging.error("Cannot find archiver `%s'.",
@@ -295,8 +295,30 @@ class ArchiveBase(Base):
validation_result = super(ArchiveBase, self)._validate_options() validation_result = super(ArchiveBase, self)._validate_options()
if 'wrapper_archive' not in self.all_options: if 'wrapper_archive' not in self.all_options:
sys.stderr.write("Configuration lacks of required " logging.warning("Configuration lacks of optional `wrapper_archive'"
"`wrapper_archive' option.\n") " option.\n")
validation_result = False wrapper_archive = self._get_wrapper_archive_name()
if wrapper_archive is None:
logging.error("Configuration lacks of optional "
"`wrapper_archive', cannot deduct the name by "
"configuration file name.\n")
validation_result = False
self.all_options['wrapper_archive'] = wrapper_archive
return validation_result return validation_result
def _get_wrapper_archive_name(self):
"""
Return full path to the archive name using configuration file
basename and appending one of the expected archive extensions.
"""
basename = os.path.splitext(self.conf_file)[0]
file_list = os.listdir('.')
for fname in file_list:
for ext in ('.7z', '.lha', '.lzx', '.zip', '.rar', '.tar', '.tgz',
'.tar.gz', '.tar.bz2', '.tar.xz'):
if ((basename + ext).lower() == fname.lower() and
basename == os.path.splitext(fname)[0]):
return fname
return None

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" """
Simple class for executing fs-uae with specified parameters. This is a Simple class for executing fs-uae with specified parameters. This is a
failsafe class for running fs-uae. failsafe class for running fs-uae.

View File

@@ -1,14 +1,12 @@
""" """
Misc utilities Misc utilities
""" """
from distutils import spawn import configparser
import logging import logging
import os import os
import pathlib
import shutil
import subprocess import subprocess
try:
import configparser
except ImportError:
import ConfigParser as configparser
from fs_uae_wrapper import message from fs_uae_wrapper import message
from fs_uae_wrapper import file_archive from fs_uae_wrapper import file_archive
@@ -22,8 +20,8 @@ class CmdOption(dict):
def add(self, option): def add(self, option):
"""parse and add option to the dictionary""" """parse and add option to the dictionary"""
if not option.startswith('--'): if not option.startswith('--'):
raise AttributeError("Cannot add option `%s' to the dictionary" % raise AttributeError(f"Cannot add option {option} to the "
option) f"dictionary")
if '=' in option: if '=' in option:
key, val = option.split('=', 1) key, val = option.split('=', 1)
key = key[2:].strip() key = key[2:].strip()
@@ -38,15 +36,15 @@ class CmdOption(dict):
ret_list = [] ret_list = []
for key, val in self.items(): for key, val in self.items():
if val != '1': if val != '1':
ret_list.append('--%(k)s=%(v)s' % {'k': key, 'v': val}) ret_list.append(f'--{key}={val}')
else: else:
ret_list.append('--%(k)s' % {'k': key}) ret_list.append(f'--{key}')
return ret_list return ret_list
def get_config_options(conf): def get_config_options(conf):
"""Read config file and return options as a dict""" """Read config file and return options as a dict"""
parser = configparser.SafeConfigParser() parser = configparser.ConfigParser()
try: try:
parser.read(conf) parser.read(conf)
except configparser.ParsingError: except configparser.ParsingError:
@@ -90,7 +88,7 @@ def create_archive(arch_name, title='', params=None):
""" """
msg = '' msg = ''
if title: if title:
msg = "Creating archive for `%s'. Please be patient" % title msg = f"Creating archive for `{title}'. Please be patient"
return operate_archive(arch_name, 'create', msg, params) return operate_archive(arch_name, 'create', msg, params)
@@ -100,7 +98,7 @@ def extract_archive(arch_name, title='', params=None):
""" """
msg = '' msg = ''
if title: if title:
msg = "Extracting files for `%s'. Please be patient" % title msg = f"Extracting files for `{title}'. Please be patient"
return operate_archive(arch_name, 'extract', msg, params) return operate_archive(arch_name, 'extract', msg, params)
@@ -146,17 +144,18 @@ def interpolate_variables(string, config_path, base=None):
_string = string _string = string
if '$CONFIG' in string: if '$CONFIG' in string:
conf_path = os.path.dirname(os.path.abspath(config_path)) conf_path = pathlib.Path(config_path).resolve().parent
string = os.path.abspath(string.replace('$CONFIG', conf_path)) string = str(pathlib.Path(string.replace('$CONFIG', str(conf_path)))
.resolve())
if '$HOME' in string: if '$HOME' in string:
string = string.replace('$HOME', os.path.expandvars('$HOME')) string = string.replace('$HOME', os.path.expandvars('$HOME'))
if '$EXE' in string: if '$EXE' in string:
string = string.replace('$EXE', spawn.find_executable('fs-uae')) string = string.replace('$EXE', shutil.which('fs-uae'))
if '$APP' in string: if '$APP' in string:
string = string.replace('$APP', spawn.find_executable('fs-uae')) string = string.replace('$APP', shutil.which('fs-uae'))
if '$DOCUMENTS' in string: if '$DOCUMENTS' in string:
xdg_docs = os.getenv('XDG_DOCUMENTS_DIR', xdg_docs = os.getenv('XDG_DOCUMENTS_DIR',

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
""" """
Wrapper for FS-UAE to perform some actions before and or after running the Wrapper for FS-UAE to perform some actions before and or after running the
emulator, if appropriate option is enabled. emulator, if appropriate option is enabled.
@@ -119,7 +118,3 @@ def run():
if not exit_code: if not exit_code:
sys.exit(4) sys.exit(4)
if __name__ == "__main__":
run()

40
pyproject.toml Normal file
View File

@@ -0,0 +1,40 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "fs-uae-wrapper"
authors = [{name = "Roman Dobosz", email = "gryf73@gmail.com"}]
license = {text = "BSD"}
description = "Automate archives support and state saves for fs-uae"
readme = "README.rst"
requires-python = ">=3.8"
keywords = ["uae", "fs-uae", "amiga", "emulator", "wrapper"]
version = "0.9.1"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: System :: Emulators",
"Topic :: Games/Entertainment"
]
[project.urls]
Homepage = "https://github.com/gryf/fs-uae-wrapper"
[project.scripts]
fs-uae-wrapper = "fs_uae_wrapper.wrapper:run"
[tool.setuptools]
py-modules = ["fs_uae_wrapper"]
[tool.distutils.bdist_wheel]
universal = true

View File

@@ -1,16 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Run CD32 games using fsuae
"""
from fs_uae_wrapper import wrapper
def main():
"""run wrapper"""
wrapper.run()
if __name__ == "__main__":
main()

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env python
"""
Setup for the fs-uae-wrapper
"""
from setuptools import setup
setup(name='fs-uae-wrapper',
packages=['fs_uae_wrapper'],
version='0.8.1',
description='Automate archives and state for fs-uae',
author='Roman Dobosz',
author_email='gryf73@gmail.com',
url='https://github.com/gryf/fs-uea-wrapper',
download_url='https://github.com/gryf/fs-uae-wrapper/archive/master.zip',
keywords=['uae', 'fs-uae', 'amiga', 'emulator', 'wrapper'],
scripts=['script/fs-uae-wrapper'],
classifiers=['Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: End Users/Desktop',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Topic :: System :: Emulators',
'Topic :: Games/Entertainment'],
long_description=open('README.rst').read(),
options={'test': {'verbose': False,
'coverage': False}})

View File

@@ -286,7 +286,7 @@ class TestBase(TestCase):
which.return_value = '7z' which.return_value = '7z'
bobj.all_options = {'wrapper': 'dummy', bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1'} 'wrapper_save_state': '1'}
self.assertFalse(bobj._validate_options()) self.assertTrue(bobj._validate_options())
bobj.all_options = {'wrapper': 'dummy', bobj.all_options = {'wrapper': 'dummy',
'wrapper_save_state': '1', 'wrapper_save_state': '1',

View File

@@ -73,7 +73,7 @@ class TestSaveState(TestCase):
self.assertFalse(arch._validate_options()) self.assertFalse(arch._validate_options())
arch.all_options['wrapper'] = 'savestate' arch.all_options['wrapper'] = 'savestate'
self.assertFalse(arch._validate_options()) self.assertTrue(arch._validate_options())
arch.all_options['wrapper_archiver'] = 'rar' arch.all_options['wrapper_archiver'] = 'rar'
self.assertTrue(arch._validate_options()) self.assertTrue(arch._validate_options())

View File

@@ -237,8 +237,8 @@ class TestCmdOptions(TestCase):
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('os.getenv') @mock.patch('os.getenv')
@mock.patch('os.path.expandvars') @mock.patch('os.path.expandvars')
@mock.patch('distutils.spawn.find_executable') @mock.patch('shutil.which')
def test_interpolate_variables(self, find_exe, expandv, getenv, os_exists): def test_interpolate_variables(self, which, expandv, getenv, os_exists):
os_exists.return_value = True os_exists.return_value = True
itrpl = utils.interpolate_variables itrpl = utils.interpolate_variables
@@ -252,7 +252,7 @@ class TestCmdOptions(TestCase):
'/home/user') '/home/user')
string = '$APP/$EXE' string = '$APP/$EXE'
find_exe.return_value = '/usr/bin/fs-uae' which.return_value = '/usr/bin/fs-uae'
self.assertEqual(itrpl(string, '/home/user/Config.fs-uae'), self.assertEqual(itrpl(string, '/home/user/Config.fs-uae'),
'/usr/bin/fs-uae//usr/bin/fs-uae') '/usr/bin/fs-uae//usr/bin/fs-uae')

View File

@@ -47,10 +47,7 @@ class TestWrapper(TestCase):
fobj.write('\n') fobj.write('\n')
wrapper.run() wrapper.run()
mock_plain_run.called_once_with('Config.fs-uae', mock_plain_run.assert_called_once()
['--fullscreen',
'--fade_out_duration=0'],
[])
# This will obviously fail for nonexistent module # This will obviously fail for nonexistent module
sys.argv.append('--wrapper=dummy_wrapper') sys.argv.append('--wrapper=dummy_wrapper')

11
tox.ini
View File

@@ -1,5 +1,5 @@
[tox] [tox]
envlist = py27,py3,py27-flake8,py3-flake8 envlist = py3,py3-flake8
usedevelop = True usedevelop = True
@@ -10,16 +10,7 @@ commands = py.test --cov=fs_uae_wrapper --cov-report=term-missing
deps = -r{toxinidir}/test-requirements.txt deps = -r{toxinidir}/test-requirements.txt
[testenv:py27]
deps = {[testenv]deps}
mock
[testenv:py3-flake8] [testenv:py3-flake8]
basepython = python3 basepython = python3
deps = flake8 deps = flake8
commands = flake8 {posargs} commands = flake8 {posargs}
[testenv:py27-flake8]
basepython = python2.7
deps = flake8
commands = flake8 {posargs}