* EDMCLogging.Logger.get_streamhandler() method to get the
logger_channel so prefs.py can call setLevel() on it.
* The top-level Logger level is always DEBUG.
* The RotatingFileHandler level is always DEBUG.
* The StreamHandler level is as configured by the user.
* EDMCLogging now creates a singleton of EDMCLogging.Logger and its
associated Logger.
* plug.py tweaked to only import EDMCLogging where it's needed for
getting/creating plugin loggers, else `import logging`.
* All logging duplicated into %TEMP%/{appname}/{logger_name}.log
* These are handled by the RotatingFileHandler, currently set to 1MiB
per file and 10 backup files.
* Do *NOT* setLevel() on the handlers, as we want to control the level
up at the logger instead. This would have caused the CL and GUI
selection of loglevel to NOT have any effect (hidden by default having
been DEBUG).
* EDMarketConnector.py now INFO logs its startup and exit.
* <module> works.
* top-level function in <module> works, presumably also any other file.
* Call from within classes works.
* Extra, commented out, test cases in EDMarketConnector.py
* Technically %(class)s can be e.g. A.B not just 'B' so say "name(s)".
* To not confuse EDMCLogging.getLogger() with logging.getLogger() it's
been renamed to get_logger().
* Note how we signal errors with finding class and/or qualname.
* Call out EDMCLogging.py in Contributing.md.
* Also now providers single caller_class_and_qualname() method to get
both strings, returned as a Tuple[str, str]. Either could be empty
if something went wrong.
* Rather than a fragile 'skip' this now:
1. Looks for the first up-stack frame with self of logging.Logger
1. Then looks for the next up-stack frame with self NOT of
logging.Logger. This should be the call site we want.
* Nothing should "from EDMarketConnector import logger" any more, so
we can move this back inside __main__ section
* We shouldn't rely on a magic number of frames to skip. Detect the
proper frame automatically.
This gets the function that called the logging, and if it's in a class
it looks up the function via getattr(<class>, <func name>) and then
uses __qualname__ to quickly get the fully qualified name.
This works by creating a temp file at config.app_dir and storing the
link in there, followed by directing the local browser to open the file.
HTML meta tags are then used to direct the browser to refresh to a URL
of our choosing (which is HTML escaped, just in case someone tries
something clever)
This should work everywhere, and on any browser (as the file:// format
is defined at https://tools.ietf.org/html/rfc1738 which was posted in
1994).
The URI used (`file://localhost/path`) ensures that we only ever
ask for a localhost file at our path.
The HTML format should be completely compliant with all major browsers
as well, ensuring that behaviour is consistent (assuming they support
HTML meta tags)
* Updater.__init__() now takes 'provider' argument to specify if we use
the internal checking code, or the available external code.
* EDMC.py changed to utilise this with internal provider.
* EDMarketConnector.py changed to use internal provider if not frozen,
else the internal provider.
* Corrected the darwin/MacOS toggling of auto updates checking to actually
use the Sparkle, not WinSparkle, API call.
* Updater.check_appcast() does the internal checking:
* class EDMCVersion to hold the information.
* Returns None on any error, or if it didn't find a newer version.
* Returns an EDMCVersion object if it found a newer version.
* Re-uses the 'About {APP}' that was defined because the MacOS version
always had an 'About' bit.
* As a side effect of wanting the copyright string in the popup this is
now defined in config.py, and setup.py has been adjusted to also take
it from there.
* The About popup includes a link to the release notes, contingent on
the git tag existing on github.
* Only one instance of the popup is allowed at once.
* There's still a 'TODO' for moving 'Check for updates' into this
popup.
Addresses issue #509
Where it was in EDMarketConnector.py caused issues because then the
main thread was blocking on the popup when other threads expected it to
be running:
---------------------------------------------------------------------
Exception in thread Journal worker:
Traceback (most recent call last):
File "C:\Users\Athan\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 926, in _bootstrap_inner
self.run()
File "C:\Users\Athan\AppData\Local\Programs\Python\Python37-32\lib\threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Athan\Documents\Devel\EDMarketConnector-python3\monitor.py", line 273, in worker
self.root.event_generate('<<JournalEvent>>', when="tail")
File "C:\Users\Athan\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1673, in event_generate
self.tk.call(args)
RuntimeError: main thread is not in main loop
---------------------------------------------------------------------
However *now* it pops up before the main UI is running properly at all,
so the EDMC window is likely out of position, and won't be
painted/themed yet.
This is deemed acceptable for a 'once a day at most' popup.
The popup title now has 'EDMC: ' at the start to be sure users know
what it's talking about.
It also has some brief advice about how to disable a plugin.
To allow users to sort out their plugins before EDMC itself moves to
Python 3.x warn them if any of their enabled, non-stock, plugins do not
have a plugin_start3() method.
* If any are found without support there's a popup triggered at the
end of AppWindow initialisation.
* Then the user can check Settings > Plugins to see a list of the
plugins without Python 3.x support.