diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 91943808..e24f8a60 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,13 +12,13 @@ assignees: '' **Please also check if the issue is covered in our [Troubleshooting Guide](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting).** It might be something with a known work around, or where a third party (such as EDSM) is causing logging that is harmless. **Please complete the following information:** - - Version [e.g. 4.0.6 - See 'Help > About E:D Market Connector'] - - OS: [e.g. Windows 10] + - Version [e.g. 4.0.6 - See 'Help > About E:D Market Connector'. If running from source using git then please paste the output of `git log --decorate=full | head -1`] + - OS: [e.g. Windows 10, Linux Debian 10.6, etc.] - OS Locale: [e.g. English, French, Serbian...] - If applicable: Browser [e.g. chrome, safari] - - Please attach log files: - 1. Always attach `%TEMP%\EDMarketConnector.log` from *immediately* after the bug occurs (re-running the application overwrites this file). - 1. If running 4.1.0 (including betas) or later: `%TEMP%\EDMarketConnector\EDMarketConnector-debug.log`. See [Debug Log File](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#debug-log-files). + - Please attach **BOTH** log files as follows: + 1. `%TEMP%\EDMarketConnector.log` from *immediately* after the bug occurs (re-running the application overwrites this file). + 1. %TEMP%\EDMarketConnector\EDMarketConnector-debug.log`. See [Debug Log File](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#debug-log-files). NB: If you don't have this log file then you're not running the latest version of the application and should update first to see if we already fixed the bug you're reporting. Use the Icon that looks like `_`, `|` and `^` all in one character towards the right of the toolbar above. diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml new file mode 100644 index 00000000..bd695779 --- /dev/null +++ b/.github/workflows/windows-build.yml @@ -0,0 +1,44 @@ +name: Build EDMC for Windows + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + test: + name: Build EDMC + runs-on: windows-2019 + + defaults: + run: + shell: powershell + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: "3.7.9" + architecture: "x86" + + - name: Install python tools + run: | + pip install wheel + pip install -r requirements-dev.txt + + - name: Download winsparkle + run: | + Invoke-Webrequest -UseBasicParsing https://github.com/vslavik/winsparkle/releases/download/v0.7.0/WinSparkle-0.7.0.zip -OutFile out.zip + Expand-Archive out.zip + Move-Item 'out\WinSparkle-0.7.0\Release\*' '.\' + + - name: Build EDMC + run: | + python setup.py py2exe + + - name: Upload build files + uses: actions/upload-artifact@v2 + with: + name: Built files + path: EDMarketConnector_win*.msi diff --git a/ChangeLog.md b/ChangeLog.md index e6d07440..e0debc15 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,62 @@ This is the master changelog for Elite Dangerous Market Connector. Entries are in reverse chronological order (latest first). --- +Release 4.1.5 +=== + +This is a minor maintenance release, mostly addressing behaviour around +process shutdown and startup, along with a couple of small enhancements that +most users won't notice. + +* If there is already an EDMarketConnector.exe process running when trying + to run another instance then that new process will no longer exit silently. + Instead you'll get a pop-up telling you it's detected another process, and + you need to close that pop-up in order for this additional process to then + exit. + + This hopefully makes it obvious when you've got a hung EDMarketConnect.exe + process that you need to kill in order to re-run the program. + +* In order to gather more information about how and why EDMarketConnector.exe + sometimes doesn't shutdown properly we've added some extra debug logging to + the sequence of clean-up calls performed during shutdown. + + Also, to make it more obvious if the process has hung during shutdown the + UI window is no longer hidden at the start of this shutdown sequence. It + will instead linger, with "Shutting down..." showing in the status line + (translation for this small phrase will be added in a later release). + + If you encounter this shutdown hang then please add a comment to + [Application can leave a zombie process on shutdown #678](https://github.com/EDCD/EDMarketConnector/issues/678) + to help us track down the cause and fix it. + +* Cater for 'mangled name' class functions in our logging code. e.g. where + you name a class member with a `__` prefix in order to 'hide' it from + out-of-class code. + +* To help track down the cause of [Crashing On Startup #798](https://github.com/EDCD/EDMarketConnector/issues/798) + we've added some exception catching in our logging code. If this is + triggered you will see `??:??` in logging output, instead of class and/or + function names. + + If you encounter this then please comment on that bug report to aid us in + tracking down the root cause! + +* Fixed logging from EDMC.exe so that the -debug log goes into `EDMC-debug.log` + not `EDMarketConnector-debug.log`. + +* Fix `EDMC.exe -j` handling of file encodings. NB: This command-line + argument isn't listed on `EDMC.exe -h` as it's intended for developer use + only. + +* Fix the name of 'Void Opal(s)' so that output of market data to files is + correct. + +* Fix URL in PLUGINS.md to refer to `main`, not `master` branch. + +* We're able to pull `py2exe` from PyPi now, so docs/Releasing.md has been + update to reflect this. + Release 4.1.4 === diff --git a/EDMC.py b/EDMC.py index fac74186..04701313 100755 --- a/EDMC.py +++ b/EDMC.py @@ -12,16 +12,20 @@ from os.path import getmtime, join from time import sleep, time from typing import TYPE_CHECKING, Any, Optional -# See EDMCLogging.py docs. # isort: off + +os.environ["EDMC_NO_UI"] = "1" + +# See EDMCLogging.py docs. +# workaround for https://github.com/EDCD/EDMarketConnector/issues/568 from EDMCLogging import edmclogger, logger, logging + if TYPE_CHECKING: from logging import trace, TRACE # type: ignore # noqa: F401 -# isort: on + edmclogger.set_channels_loglevel(logging.INFO) -# workaround for https://github.com/EDCD/EDMarketConnector/issues/568 -os.environ["EDMC_NO_UI"] = "1" +# isort: on import collate import commodity @@ -169,7 +173,16 @@ sys.path: {sys.path}''' if args.j: logger.debug('Import and collate from JSON dump') # Import and collate from JSON dump - data = json.load(open(args.j)) + # + # Try twice, once with the system locale and once enforcing utf-8. If the file was made on the current + # system, chances are its the current locale, and not utf-8. Otherwise if it was copied, its probably + # utf8. Either way, try the system FIRST because reading something like cp1251 in UTF-8 results in garbage + # but the reverse results in an exception. + try: + data = json.load(open(args.j)) + except UnicodeDecodeError: + data = json.load(open(args.j, encoding='utf-8')) + config.set('querytime', int(getmtime(args.j))) else: @@ -184,7 +197,7 @@ sys.path: {sys.path}''' logfile = join(logdir, logfiles[-1]) logger.debug(f'Using logfile "{logfile}"') - with open(logfile, 'r') as loghandle: + with open(logfile, 'r', encoding='utf-8') as loghandle: for line in loghandle: try: monitor.parse_entry(line) diff --git a/EDMCLogging.py b/EDMCLogging.py index 308e85fc..eae1a222 100644 --- a/EDMCLogging.py +++ b/EDMCLogging.py @@ -284,7 +284,25 @@ class EDMCContextFilter(logging.Filter): caller_qualname = caller_class_names = '' if frame: # - frame_info = inspect.getframeinfo(frame) + try: + frame_info = inspect.getframeinfo(frame) + # raise(IndexError) # TODO: Remove, only for testing + + except Exception: + # Separate from the print below to guarantee we see at least this much. + print('EDMCLogging:EDMCContextFilter:caller_attributes(): Failed in `inspect.getframinfo(frame)`') + + # We want to *attempt* to show something about the nature of 'frame', + # but at this point we can't trust it will work. + try: + print(f'frame: {frame}') + + except Exception: + pass + + # We've given up, so just return all '??' to signal we couldn't get the info + return '??', '??', '??' + args, _, _, value_dict = inspect.getargvalues(frame) if len(args) and args[0] in ('self', 'cls'): frame_class: 'object' = value_dict[args[0]] diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 63cc2f6f..0607afaa 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -17,8 +17,117 @@ from typing import TYPE_CHECKING from config import applongname, appname, appversion, appversion_nobuild, config, copyright -# TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup if __name__ == "__main__": + def no_other_instance_running() -> bool: # noqa: CCR001 + """ + Ensure only one copy of the app is running under this user account. + + OSX does this automatically. + + :returns: True if we are the single instance, else False. + """ + # TODO: Linux implementation + if platform == 'win32': + import ctypes + from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR + + EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806 + GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806 + GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int] + GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806 + GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int] + GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806 + GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806 + + SW_RESTORE = 9 # noqa: N806 + SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow # noqa: N806 + ShowWindow = ctypes.windll.user32.ShowWindow # noqa: N806 + ShowWindowAsync = ctypes.windll.user32.ShowWindowAsync # noqa: N806 + + COINIT_MULTITHREADED = 0 # noqa: N806,F841 + COINIT_APARTMENTTHREADED = 0x2 # noqa: N806 + COINIT_DISABLE_OLE1DDE = 0x4 # noqa: N806 + CoInitializeEx = ctypes.windll.ole32.CoInitializeEx # noqa: N806 + + ShellExecute = ctypes.windll.shell32.ShellExecuteW # noqa: N806 + ShellExecute.argtypes = [HWND, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, INT] + + def window_title(h): + if h: + text_length = GetWindowTextLength(h) + 1 + buf = ctypes.create_unicode_buffer(text_length) + if GetWindowText(h, buf, text_length): + return buf.value + + return None + + @ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM) + def enumwindowsproc(window_handle, l_param): + # class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576 + cls = ctypes.create_unicode_buffer(257) + if GetClassName(window_handle, cls, 257) \ + and cls.value == 'TkTopLevel' \ + and window_title(window_handle) == applongname \ + and GetProcessHandleFromHwnd(window_handle): + # If GetProcessHandleFromHwnd succeeds then the app is already running as this user + if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler.redirect): + CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) + # Wait for it to be responsive to avoid ShellExecute recursing + ShowWindow(window_handle, SW_RESTORE) + ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE) + + else: + ShowWindowAsync(window_handle, SW_RESTORE) + SetForegroundWindow(window_handle) + + return False + + return True + + return EnumWindows(enumwindowsproc, 0) + + return True + + def already_running_popup(): + """Create the "already running" popup.""" + import tkinter as tk + from tkinter import ttk + + root = tk.Tk(className=appname.lower()) + + frame = tk.Frame(root) + frame.grid(row=1, column=0, sticky=tk.NSEW) + + label = tk.Label(frame) + label['text'] = 'An EDMarketConnector.exe process was already running, exiting.' + label.grid(row=1, column=0, sticky=tk.NSEW) + + button = ttk.Button(frame, text='OK', command=lambda: sys.exit(0)) + button.grid(row=2, column=0, sticky=tk.S) + + root.mainloop() + + if not no_other_instance_running(): + # There's a copy already running. We want to inform the user by + # **appending** to the log file, not truncating it. + if getattr(sys, 'frozen', False): + # By default py2exe tries to write log to dirname(sys.executable) which fails when installed + import tempfile + + # unbuffered not allowed for text in python3, so use `1 for line buffering + sys.stdout = sys.stderr = open(join(tempfile.gettempdir(), f'{appname}.log'), mode='a', buffering=1) + + # Logging isn't set up yet. + # We'll keep this print, but it will be over-written by any subsequent + # write by the already-running process. + print("An EDMarketConnector.exe process was already running, exiting.") + + # To be sure the user knows, we need a popup + already_running_popup() + # If the user closes the popup with the 'X', not the 'OK' button we'll + # reach here. + sys.exit(0) + # Keep this as the very first code run to be as sure as possible of no # output until after this redirect is done, if needed. if getattr(sys, 'frozen', False): @@ -36,6 +145,9 @@ if TYPE_CHECKING: from logging import trace, TRACE # type: ignore # noqa: F401 # isort: on + def _(x: str) -> str: + """Fake the l10n translation functions for typing.""" + return x if getattr(sys, 'frozen', False): # Under py2exe sys.path[0] is the executable name @@ -947,18 +1059,43 @@ class AppWindow(object): if platform != 'darwin' or self.w.winfo_rooty() > 0: x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267' config.set('geometry', f'+{x}+{y}') - self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen + + # Let the user know we're shutting down. + self.status['text'] = _('Shutting down...') + self.w.update_idletasks() + logger.info('Starting shutdown procedures...') + + logger.info('Closing protocol handler...') protocolhandler.close() + + logger.info('Unregistering hotkey manager...') hotkeymgr.unregister() + + logger.info('Closing dashboard...') dashboard.close() + + logger.info('Closing journal monitor...') monitor.close() + + logger.info('Notifying plugins to stop...') plug.notify_stop() + + logger.info('Closing update checker...') self.updater.close() + + logger.info('Closing Frontier CAPI sessions...') companion.session.close() + + logger.info('Closing config...') config.close() + + logger.info('Destroying app window...') self.w.destroy() - def drag_start(self, event): + logger.info('Done.') + + def drag_start(self, event) -> None: + """Initiate dragging the window.""" self.drag_offset = (event.x_root - self.w.winfo_rootx(), event.y_root - self.w.winfo_rooty()) def drag_continue(self, event): diff --git a/L10n/en.template b/L10n/en.template index cf14aa37..262b36d9 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -454,6 +454,9 @@ /* Setting to decide which ship outfitting website to link to - either E:D Shipyard or Coriolis. [prefs.py] */ "Shipyard" = "Shipyard"; +/* Status line text that appears when process exit sequence starts [EDMarketConnector.py] */ +"Shutting down..." = "Shutting down..."; + /* Empire rank. [stats.py] */ "Squire" = "Squire"; diff --git a/PLUGINS.md b/PLUGINS.md index 717171f7..00d48155 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -21,6 +21,13 @@ Plugins are python files. The plugin folder must have a file named `load.py` that must provide one module level function and optionally provide a few others. +If you're running from source (which allows for debugging with e.g. [PyCharm](https://www.jetbrains.com/pycharm/features/)) +then you'll need to be using an appropriate version of Python. The current +version is listed in the [Environment section of Releasing.md](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Releasing.md#environment). +If you're developing your plugin simply against an install of EDMarketConnector +then you'll be relying on the bundled version of Python (it's baked +into the .exe via the py2exe build process). + --- ### Examples @@ -351,7 +358,7 @@ threads - doing so may cause the app to crash intermittently. You can signal back to the main thread using Tkinter's `event_generate()` widget method, generating a user-defined event that you have previously registered with the [`bind_all()`](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm) -widget method. See the [EDSM plugin](https://github.com/Marginal/EDMarketConnector/blob/master/plugins/edsm.py) +widget method. See the [EDSM plugin](https://github.com/Marginal/EDMarketConnector/blob/main/plugins/edsm.py) for an example of these techniques. #### Journal Entry diff --git a/commodity.csv b/commodity.csv index a78111a8..eb7084a3 100644 --- a/commodity.csv +++ b/commodity.csv @@ -215,6 +215,6 @@ id,symbol,category,name 128924329,Benitoite,Minerals,Benitoite 128924330,Grandidierite,Minerals,Grandidierite 128924331,Alexandrite,Minerals,Alexandrite -128924332,Opal,Minerals,Void Opals +128924332,Opal,Minerals,Void Opal 128924333,RockforthFertiliser,Chemicals,Rockforth Fertiliser 128924334,AgronomicTreatment,Chemicals,Agronomic Treatment diff --git a/config.py b/config.py index 880f559d..e60ff1cd 100644 --- a/config.py +++ b/config.py @@ -13,7 +13,7 @@ appcmdname = 'EDMC' # appversion **MUST** follow Semantic Versioning rules: # # Major.Minor.Patch(-prerelease)(+buildmetadata) -appversion = '4.1.4' #-rc1+a872b5f' +appversion = '4.1.5' #-rc1+a872b5f' # For some things we want appversion without (possible) +build metadata appversion_nobuild = str(semantic_version.Version(appversion).truncate('prerelease')) copyright = u'© 2015-2019 Jonathan Harris, 2020 EDCD' diff --git a/docs/Automatic Builds.md b/docs/Automatic Builds.md new file mode 100644 index 00000000..014ecc3c --- /dev/null +++ b/docs/Automatic Builds.md @@ -0,0 +1,28 @@ +# Introduction + +Instead of building manually as laid out by [Releasing](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Releasing.md), you can build the EDMC installer using GitHub actions. + +## Initiating a workflow run + +Starting a workflow run is done from the Actions tab at the top of the main GitHub UI + +NB: The branch you want to build must have the workflow file (`.github/workflows/windows-build.yml`), and the version of the file in that branch is the version that will be used for the build (e.g. for different python versions) + +1. Select the Actions tab at the top of the main GitHub interface +2. Select the `Build EDMC for Windows` workflow on the left +3. Click the "Run workflow" button at the right side of the blue banner + 1. Select the branch you want to build + 2. Click the "Run Workflow" + +## Downloading built installer files + +When the workflow is (successfully) completed, it will upload the msi file it built as a "Workflow Artifact". You can find the artifacts as follows: + +1. Select `All workflows` on the left +2. Select the `Build EDMC for Windows` action +3. Select your build (probably the top one) +4. Find the `Built Files` artifact + +Within the `Built Files` zip file is the installer msi + +**Please ensure you test the built msi before creating a release.** diff --git a/docs/Releasing.md b/docs/Releasing.md index b340e256..846324e2 100644 --- a/docs/Releasing.md +++ b/docs/Releasing.md @@ -10,6 +10,8 @@ This document aims to enable anyone to quickly get up to speed on how to: Note that for Windows only a 32-bit application is supported at this time. This is principally due to the Windows Registry handling in config.py. +Builds can be run automatically from GitHub Actions. For more information on that process, see [Automatic Builds](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Automatic%20Builds.md). + # Environment You will need several pieces of software installed, or the files from their @@ -33,29 +35,16 @@ You will need several pieces of software installed, or the files from their [v3.7.9](https://www.python.org/downloads/release/python-379/) is the most recently tested version. You need the `Windows x86 executable installer` file, for the 32-bit version. -1. [py2exe](https://github.com/albertosottile/py2exe): - 1. Install the python module. There are two options here. - 1. You can use the latest release version [0.9.3.2](https://github.com/albertosottile/py2exe/releases/tag/v0.9.3.2) - and the current Marginal 'python3' branch as-is. This contains a - small hack in `setup.py` to ensure `sqlite3.dll` is packaged. - - pip install py2exe-0.9.3.2-cp37-none-win32.whl - 1. Or you can use a pre-release version, [0.9.4.0](https://bintray.com/alby128/py2exe/download_file?file_path=py2exe-0.9.4.0-cp37-none-win32.whl), see [this py2exe issue](https://github.com/albertosottile/py2exe/issues/23#issuecomment-541359225), - which packages that DLL file correctly. - - pip install py2exe-0.9.4.0-cp37-none-win32.whl - You can then edit out the following line from `setup.py`, but it - does no harm: - - %s/DLLs/sqlite3.dll' % (sys.base_prefix), +1. [py2exe](https://github.com/albertosottile/py2exe) - Now available via PyPi, + so will be picked up with the `pip install` below. Latest tested as per + `requirements-dev.txt`. 1. You'll now need to 'pip install' several python modules. 1. Ensure you have `pip` installed. If needs be see [Installing pip](https://pip.pypa.io/en/stable/installing/) 1. The easiest way is to utilise the `requirements-dev.txt` file: - `python -m pip install -r requirements-dev.txt`. This will install all - dependencies plus anything required for development *other than py2exe, see - above*. + `python -m pip install --user -r requirements-dev.txt`. This will install + all dependencies plus anything required for development. 1. Else check the contents of both `requirements.txt` and `requirements-dev.txt`, and ensure the modules listed there are installed as per the version requirements. diff --git a/edmarketconnector.xml b/edmarketconnector.xml index 83782562..a696a1e4 100644 --- a/edmarketconnector.xml +++ b/edmarketconnector.xml @@ -168,11 +168,73 @@ - Release 4.1.4 + Release 4.1.5 body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; } +

Release 4.1.5

+

This is a minor maintenance release, mostly addressing behaviour around +process shutdown and startup, along with a couple of small enhancements that +most users won't notice.

+
    +
  • +

    If there is already an EDMarketConnector.exe process running when trying +to run another instance then that new process will no longer exit silently. +Instead you'll get a pop-up telling you it's detected another process, and +you need to close that pop-up in order for this additional process to then +exit.

    +

    This hopefully makes it obvious when you've got a hung EDMarketConnect.exe +process that you need to kill in order to re-run the program.

    +
  • +
  • +

    In order to gather more information about how and why EDMarketConnector.exe +sometimes doesn't shutdown properly we've added some extra debug logging to +the sequence of clean-up calls performed during shutdown.

    +

    Also, to make it more obvious if the process has hung during shutdown the +UI window is no longer hidden at the start of this shutdown sequence. It +will instead linger, with "Shutting down..." showing in the status line +(translation for this small phrase will be added in a later release).

    +

    If you encounter this shutdown hang then please add a comment to +Application can leave a zombie process on shutdown #678 +to help us track down the cause and fix it.

    +
  • +
  • +

    Cater for 'mangled name' class functions in our logging code. e.g. where +you name a class member with a __ prefix in order to 'hide' it from +out-of-class code.

    +
  • +
  • +

    To help track down the cause of Crashing On Startup #798 +we've added some exception catching in our logging code. If this is +triggered you will see ??:?? in logging output, instead of class and/or +function names.

    +

    If you encounter this then please comment on that bug report to aid us in +tracking down the root cause!

    +
  • +
  • +

    Fixed logging from EDMC.exe so that the -debug log goes into EDMC-debug.log +not EDMarketConnector-debug.log.

    +
  • +
  • +

    Fix EDMC.exe -j handling of file encodings. NB: This command-line +argument isn't listed on EDMC.exe -h as it's intended for developer use +only.

    +
  • +
  • +

    Fix the name of 'Void Opal(s)' so that output of market data to files is +correct.

    +
  • +
  • +

    Fix URL in PLUGINS.md to refer to main, not master branch.

    +
  • +
  • +

    We're able to pull py2exe from PyPi now, so docs/Releasing.md has been +update to reflect this.

    +
  • +
+ +

Release 4.1.4

The only change from 4.1.3 is to insert some Windows version checks before even attempting to set a UTF-8 encoding. We'll now only attempt this if the @@ -824,11 +886,11 @@ If any of your plugins are listed in that section then they will need updating, ]]> diff --git a/setup.py b/setup.py index 12953b0f..6d71fb6b 100755 --- a/setup.py +++ b/setup.py @@ -222,36 +222,3 @@ elif sys.platform == 'win32': os.system(r'cscript /nologo "%s\WiSubStg.vbs" %s %s\%d.mst %d' % (SDKPATH, PKG, gettempdir(), lcid, lcid)) else: raise AssertionError('Unsupported platform') - -if not exists(PKG): - raise AssertionError('No %s found prior to appcast' % (PKG)) -# Make appcast entry -appcast = open('appcast_%s_%s.xml' % (sys.platform=='darwin' and 'mac' or 'win', VERSION), 'w') -appcast.write(''' -\t\t -\t\t\tRelease {VERSION} -\t\t\t -\t\t\t\t{STYLE} -

Release {VERSION}

-
    - -
-\t\t\t\t]]> -\t\t\t
-\t\t\t -\t\t
-'''.format(VERSION=VERSION, - STYLE='{}'.format( - sys.platform=='win32' and 'body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }' - or 'h2 { font-size: 105%; }'), - PKG=PKG, - OS=''.format(sys.platform=='win32' and 'windows"\n\t\t\t\tsparkle:installerArguments="/passive LAUNCH=yes' or 'macos'), - LENGTH=os.stat(PKG).st_size) -)