Commit Graph
6458 Commits
Author SHA1 Message Date
Inutt 26e3407f53 Revert "Add Multicrew Check Too"
This reverts commit a8e34f2a3b.
2025-12-05 00:20:14 +00:00
Inutt 733a518341 Set player's multicrew role to idle when physically boarding another player's ship 2025-12-04 23:56:07 +00:00
David Sangrey 0e28cd4962 [Minor] Remove Unneeded Keys Calls 2025-12-01 22:08:16 -05:00
David Sangrey 2041b3d3d3 [Minor] Update Type Hints and Alias Usages 2025-12-01 22:04:28 -05:00
David Sangrey b63b4b90ca [Minor] Move Linter Warning 2025-12-01 21:54:14 -05:00
David Sangrey a9693dca4c Merge pull request #2534 from inutt/fix/2345/relative-imports
Fix/2345/relative imports
2025-12-01 21:51:36 -05:00
David Sangrey 1beddea8e0 Merge pull request #2538 from inutt/fix/--/linting
Fix mypy linting issues
2025-12-01 21:51:20 -05:00
David Sangrey 88c21ec883 Merge branch 'develop' into fix/2345/relative-imports 2025-12-01 21:49:52 -05:00
David Sangrey 6d9b200ea8 [#2535] Redo CSV Exporter 2025-12-01 21:46:47 -05:00
David Sangrey 04bbedff29 Merge pull request #2548 from EDCD/dependabot/pip/develop/mypy-1.19.0
Bump mypy from 1.18.2 to 1.19.0
2025-12-01 20:57:27 -05:00
David Sangrey 92601d7b7f Merge pull request #2540 from inutt/enhancement/2404/localised-system-colonisation-ship-name
Add localisation of stations names
2025-12-01 20:46:02 -05:00
David Sangrey 98d37d7676 Merge pull request #2544 from EDCD/dependabot/github_actions/develop/actions/checkout-6
Bump actions/checkout from 5 to 6
2025-12-01 20:45:09 -05:00
David Sangrey fc139c0c5d Merge pull request #2546 from EDCD/dependabot/pip/develop/pytest-9.0.1
Bump pytest from 9.0.0 to 9.0.1
2025-12-01 20:33:38 -05:00
David Sangrey aa7beceef8 Merge pull request #2545 from EDCD/dependabot/pip/develop/pre-commit-4.5.0
Bump pre-commit from 4.4.0 to 4.5.0
2025-12-01 20:33:29 -05:00
David Sangrey 9d374ab612 Merge pull request #2547 from EDCD/dependabot/pip/develop/coverage-toml--7.12.0
Bump coverage[toml] from 7.11.2 to 7.12.0
2025-12-01 20:33:20 -05:00
dependabot[bot] 834158a3c7 Bump mypy from 1.18.2 to 1.19.0
Bumps [mypy](https://github.com/python/mypy) from 1.18.2 to 1.19.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.18.2...v1.19.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.19.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:41 +00:00
dependabot[bot] 252e16cade Bump coverage[toml] from 7.11.2 to 7.12.0
Bumps [coverage[toml]](https://github.com/coveragepy/coveragepy) from 7.11.2 to 7.12.0.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.2...7.12.0)

---
updated-dependencies:
- dependency-name: coverage[toml]
  dependency-version: 7.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:36 +00:00
dependabot[bot] 77eee9e8be Bump pytest from 9.0.0 to 9.0.1
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.0 to 9.0.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.0...9.0.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:30 +00:00
dependabot[bot] 98f1aed3f0 Bump pre-commit from 4.4.0 to 4.5.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.4.0...v4.5.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 19:12:23 +00:00
dependabot[bot] 4fc73c39cd Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 18:49:07 +00:00
David Sangrey 28cc39fd1d Merge pull request #2542 from inutt/fix/2541/preferences-path-display
Fix preference dialog not detecting tab change events
2025-11-30 21:10:33 -05:00
Inutt c5961cd91c Fix preference dialog not detecting tab change events 2025-11-29 03:26:21 +00:00
Inutt 9fd04e5c02 Add localisation of stations names
This currently only affects system colonisation ships, which have '$EXT_PANEL_ColonisationShip;' in their StationName
entries in the journal. Other strings may need to be added in future.

Fixes #2404
diff --git a/L10n/en.template b/L10n/en.template
index faac3259..f670e39c 100644
--- a/L10n/en.template
+++ b/L10n/en.template
@@ -839,3 +839,9 @@

 /* edastro_core.py: Settings>EDAstro - Label on checkbox for 'send data'; */
 "Send data to EDAstro" = "Send data to EDAstro";
+
+/* common_coreutils.py: Colonisation ship name, with title; In files: common_coreutils.py:81:81 */
+"System Colonisation Ship {NAME}" = "System Colonisation Ship {NAME}";
+
+/* common_coreutils.py: Unidentified colonisation ship; In files: common_coreutils.py:84:84 */
+"System Colonisation Ship" = "System Colonisation Ship";
diff --git a/plugins/common_coreutils.py b/plugins/common_coreutils.py
index 6b90d4dc..a488de46 100644
--- a/plugins/common_coreutils.py
+++ b/plugins/common_coreutils.py
@@ -29,6 +29,7 @@ import gzip
 import io
 import json
 import os
+import re
 import myNotebook as nb  # noqa: N813
 from EDMCLogging import get_main_logger
 from companion import CAPIData
@@ -47,6 +48,49 @@ SEPY = 10  # seperator line spacing
 STATION_UNDOCKED = '×'  # "Station" name to display when not docked = U+00D7

+# Known colonisation ship names match one of the following:
+#   "$EXT_PANEL_ColonisationShip;"
+#   "$EXT_PANEL_ColonisationShip; <ship name>" with "<ship name>" being eg; "Okawara Horizons"
+#   "$EXT_PANEL_ColonisationShip:#index=<number>;"
+#   "$EXT_PANEL_ColonisationShip:#index=<number>; <ship name>"
+_RE_COLONISATION_SHIP = re.compile(r'\$EXT_PANEL_ColonisationShip(?::#index=(\d+))?;(?: (.+))?')
+
+
+def localised_name(value: str | None) -> str | None:
+    """
+    Handle localisation of strings in places like station names.
+
+    This is currently an issue for system colonisation ships that have a station name along the
+    lines of "$EXT_PANEL_ColonisationShip; Okawara Horizons" when written to the journal, but would
+    be better displayed as "System Colonisation Ship Okawara Horizona" (or other appropriately
+    localised equivalent).
+
+    If no (known) localisable strings are present, the input will be returned unmodified.
+
+    :param value: The non-localised string.
+    :return: The localised string, or None if the input was None.
+    """
+    if value is None:
+        return None
+
+    colonisation_ship_match = _RE_COLONISATION_SHIP.match(value)
+    if colonisation_ship_match:
+        ship_index = colonisation_ship_match.group(1)
+        ship_name = colonisation_ship_match.group(2)
+        # Use the ship's index as its name if no name was provided.
+        ship_id = ship_name or ship_index
+
+        if ship_id:
+            # LANG: Colonisation ship name, with title
+            value = tr.tl("System Colonisation Ship {NAME}").format(NAME=ship_id)
+
+        else:
+            # LANG: Unidentified colonisation ship
+            value = tr.tl("System Colonisation Ship")
+
+    return value
+
+
 def plugin_start3(plugin_dir: str) -> str:
     """
     Start the plugin.
@@ -121,7 +165,7 @@ def station_link_common(data: CAPIData, this):
     :param this: The module global from the calling module.
     """
     if data['commander']['docked'] or this.on_foot and this.station_name:
-        this.station_link['text'] = this.station_name
+        this.station_link['text'] = localised_name(this.station_name)
     elif data['lastStarport']['name'] and data['lastStarport']['name'] != "":
         this.station_link['text'] = STATION_UNDOCKED
     else:
@@ -163,12 +207,14 @@ def station_name_setter_common(this):
     :param this: The module global from the calling module.
     """
     to_set: str = cast(str, this.station_name)
+
     if not to_set:
         if this.system_population is not None and this.system_population > 0:
             to_set = STATION_UNDOCKED
         else:
             to_set = ''

+    to_set = str(localised_name(to_set))
     this.station_link['text'] = to_set

diff --git a/plugins/edsm.py b/plugins/edsm.py
index e1e6b168..902e248b 100644
--- a/plugins/edsm.py
+++ b/plugins/edsm.py
@@ -43,7 +43,7 @@ from ttkHyperlinkLabel import HyperlinkLabel
 from l10n import translations as tr
 from plugins.common_coreutils import (api_keys_label_common, PADX, PADY, BUTTONX, SEPY, BOXY, STATION_UNDOCKED,
                                       show_pwd_var_common, station_link_common, this_format_common,
-                                      cmdr_data_initial_common)
+                                      cmdr_data_initial_common, localised_name)

 # TODO:
@@ -543,7 +543,7 @@ entry: {entry!r}'''
                 to_set = ''

         if this.station_link:
-            this.station_link['text'] = to_set
+            this.station_link['text'] = localised_name(to_set)
             this.station_link['url'] = station_url(str(this.system_name), str(this.station_name))
             this.station_link.update_idletasks()
2025-11-29 01:25:19 +00:00
Inutt bf5824223d Fix mypy linting issues
Mostly related to adjusting `if sys.platform == 'win32'` logic so things that are only defined on win32 platforms don't
cause issues on other platforms.
2025-11-28 04:32:52 +00:00
Inutt f4e70dbe5b Add documentation for relative plugin imports
diff --git a/PLUGINS.md b/PLUGINS.md
index 6fd20ef9..71aaec7a 100644
--- a/PLUGINS.md
+++ b/PLUGINS.md
@@ -163,6 +163,19 @@ circular imports.
 For an example of how this is done, look at the code in `plugins/common_coreutils.py`
 and the usage of these functions in other core plugins.

+Plugins may import additional code modules from `load.py` in the usual way. Note
+that using an import statement like `import my_module` or `from my_module import
+foo` will only import one module with that name **across all plugins**. This can
+be used to create a shared library used by several plugins if used carefully,
+but may create a conflict if another plugin also has a module of the same name
+containing different code. For example if two plugins both contain a
+`widgets.py` file and both do `import widgets`, only one of the two modules will
+actually be imported.
+
+To avoid this issue, use a relative import statement such as `import .widgets`
+or `from .widgets import foo` to ensure that the module is loaded from the
+current plugin, even if the name conflicts with another plugin's code.
+
 ---

 ## Logging
2025-11-24 20:14:51 +00:00
Inutt b9f3cf3d6a Fix relative imports in plugins
Allow plugins to use relative imports to avoid conflicts between modules with the same name from another plugin.

Fixes #2345
diff --git a/plug.py b/plug.py
index 1a8779f7..8890c80f 100644
--- a/plug.py
+++ b/plug.py
@@ -48,13 +48,20 @@ last_error = LastError()
 class Plugin:
     """An EDMC plugin."""

-    def __init__(self, name: str, loadfile: Path | None, plugin_logger: logging.Logger | None):  # noqa: CCR001
+    def __init__(
+        self,
+        name: str,
+        loadfile: Path | None,
+        plugin_logger: logging.Logger | None,
+        internal: bool = False
+    ):  # noqa: CCR001
         """
         Load a single plugin.

         :param name: Base name of the file being loaded from.
         :param loadfile: Full path/filename of the plugin.
         :param plugin_logger: The logging instance for this plugin to use.
+        :param internal: True to use internal plugin loader logic. Defaults to False.
         :raises Exception: Typically, ImportError or OSError
         """
         self.name: str = name  # Display name.
@@ -65,14 +72,19 @@ class Plugin:
         if loadfile:
             logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"')
             try:
-                filename = 'plugin_'
-                filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
-                spec = importlib.util.spec_from_file_location(filename, loadfile)
-                # Replaces older load_module() code. Includes a safety check that the module name is set.
-                if spec is not None and spec.loader is not None:
-                    module = importlib.util.module_from_spec(spec)
-                    sys.modules[module.__name__] = module
-                    spec.loader.exec_module(module)
+                if internal:
+                    filename = 'plugin_'
+                    filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
+                    spec = importlib.util.spec_from_file_location(filename, loadfile)
+                    # Replaces older load_module() code. Includes a safety check that the module name is set.
+                    if spec is not None and spec.loader is not None:
+                        module = importlib.util.module_from_spec(spec)
+                        sys.modules[module.__name__] = module
+                        spec.loader.exec_module(module)
+                else:
+                    module = importlib.import_module('.load', name)
+
+                if module:
                     if getattr(module, 'plugin_start3', None):
                         newname = module.plugin_start3(Path(loadfile).resolve().parent)
                         self.name = str(newname) if newname else self.name
@@ -170,7 +182,7 @@ def _load_internal_plugins():
             try:
                 plugin_name = name[:-3]
                 plugin_path = config.internal_plugin_dir_path / name
-                plugin = Plugin(plugin_name, plugin_path, logger)
+                plugin = Plugin(plugin_name, plugin_path, logger, internal=True)
                 plugin.folder = None
                 internal.append(plugin)
             except Exception:

diff --git a/plug.py b/plug.py
index 1a8779f7..8890c80f 100644
--- a/plug.py
+++ b/plug.py
@@ -48,13 +48,20 @@ last_error = LastError()
 class Plugin:
     """An EDMC plugin."""

-    def __init__(self, name: str, loadfile: Path | None, plugin_logger: logging.Logger | None):  # noqa: CCR001
+    def __init__(
+        self,
+        name: str,
+        loadfile: Path | None,
+        plugin_logger: logging.Logger | None,
+        internal: bool = False
+    ):  # noqa: CCR001
         """
         Load a single plugin.

         :param name: Base name of the file being loaded from.
         :param loadfile: Full path/filename of the plugin.
         :param plugin_logger: The logging instance for this plugin to use.
+        :param internal: True to use internal plugin loader logic. Defaults to False.
         :raises Exception: Typically, ImportError or OSError
         """
         self.name: str = name  # Display name.
@@ -65,14 +72,19 @@ class Plugin:
         if loadfile:
             logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"')
             try:
-                filename = 'plugin_'
-                filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
-                spec = importlib.util.spec_from_file_location(filename, loadfile)
-                # Replaces older load_module() code. Includes a safety check that the module name is set.
-                if spec is not None and spec.loader is not None:
-                    module = importlib.util.module_from_spec(spec)
-                    sys.modules[module.__name__] = module
-                    spec.loader.exec_module(module)
+                if internal:
+                    filename = 'plugin_'
+                    filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_')
+                    spec = importlib.util.spec_from_file_location(filename, loadfile)
+                    # Replaces older load_module() code. Includes a safety check that the module name is set.
+                    if spec is not None and spec.loader is not None:
+                        module = importlib.util.module_from_spec(spec)
+                        sys.modules[module.__name__] = module
+                        spec.loader.exec_module(module)
+                else:
+                    module = importlib.import_module('.load', name)
+
+                if module:
                     if getattr(module, 'plugin_start3', None):
                         newname = module.plugin_start3(Path(loadfile).resolve().parent)
                         self.name = str(newname) if newname else self.name
@@ -170,7 +182,7 @@ def _load_internal_plugins():
             try:
                 plugin_name = name[:-3]
                 plugin_path = config.internal_plugin_dir_path / name
-                plugin = Plugin(plugin_name, plugin_path, logger)
+                plugin = Plugin(plugin_name, plugin_path, logger, internal=True)
                 plugin.folder = None
                 internal.append(plugin)
             except Exception:
2025-11-24 18:00:58 +00:00
David Sangrey 48dafaf31f Update ChangeLog.md 2025-11-20 18:19:04 -05:00
David Sangrey 459a794460 [PRE-RELEASE] 6.0.0-beta2 Release/6.0.0-beta2 2025-11-20 18:03:58 -05:00
David Sangrey a8e34f2a3b Add Multicrew Check Too 2025-11-20 18:00:06 -05:00
David Sangrey b85705c13a [#2532] Ignore Loadout If Embarked 2025-11-20 17:53:57 -05:00
David Sangrey 691e0a66ad Merge pull request #2531 from dvdmuckle/develop
Improve Linux config import
2025-11-19 22:19:11 -05:00
David Muckle f2d2354fb5 Improve linux config import and logging 2025-11-19 21:50:34 -05:00
David Sangrey 8dbd5cae69 [Minor] Add Custom Config Path, Add Additional Logging 2025-11-19 20:57:41 -05:00
David Sangrey 3fee941bbb [Minor] Fix Some Output Tooling 2025-11-18 22:17:26 -05:00
David Sangrey bc09786b28 [RELEASE] 6.0.0-beta1 Release/6.0.0-beta1 2025-11-18 21:50:56 -05:00
David Sangrey 61e8ff0c7a [Minor] Fix "Reset Provider" popup on first run 2025-11-18 18:56:37 -05:00
David Sangrey 4bd6ec6d7a Merge pull request #2528 from HullSeals/enhancement/261/disable-plugins
Scrollable Notebook and Plugin Enable/Disable System
2025-11-18 18:39:23 -05:00
David Sangrey d545a78b1f Merge branch 'beta' into enhancement/261/disable-plugins 2025-11-17 20:55:49 -05:00
David Sangrey 7a56ea78dc [5.13.3] Update Changelog Release/5.13.3 2025-11-17 19:42:48 -05:00
Ben Peddell f80ac51fe2 [Bugfix] Fix race starting journal monitor
Fixes a race identified where long plugin init times can result in
the following error being thrown in the journal monitor thread,
requiring a preferences reload to have journals continuing to be
processed:

```
Exception in thread Journal worker:
Traceback (most recent call last):
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/threading.py", line 1045, in _bootstrap_inner
    self.run()
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/threading.py", line 982, in run
    self._target(*self._args, **self._kwargs)
  File "/home/testing/gitstuff/EDMarketConnector/monitor.py", line 461, in worker
    self.root.event_generate('<<JournalEvent>>', when="tail")
  File "/home/testing/.pyenv/versions/3.11.9/lib/python3.11/tkinter/__init__.py", line 1933, in event_generate
    self.tk.call(args)
RuntimeError: main thread is not in main loop
```
2025-11-17 19:34:05 -05:00
David Sangrey ca243b775f [#261] Enable Plugin Enable/Disable System
This does rely on plugins respecting a stop call.
2025-11-17 19:12:12 -05:00
David Sangrey 84dc66c8ac [#1293] Create Scrollable Notebook 2025-11-17 19:05:24 -05:00
David Sangrey 435e0d9bd3 Merge pull request #2527 from HullSeals/enhancement/517/config-unification
New Config System
2025-11-17 19:02:02 -05:00
David Sangrey 0f5ab3e706 Update windows-build.yml 2025-11-17 00:00:38 -05:00
David Sangrey 7b97f29867 [Minor] I shouldn't code late at night 2025-11-16 23:54:41 -05:00
David Sangrey 9d2d02ae47 [1790] Rename Output Zip 2025-11-16 23:47:33 -05:00
David Sangrey ef355f5745 Revert Bad Change 2025-11-16 23:33:36 -05:00
David Sangrey 0c175506e7 Merge branch 'enhancement/517/config-unification' of https://github.com/HullSeals/EDMarketConnector into enhancement/517/config-unification 2025-11-16 23:30:38 -05:00
David Sangrey c1229b60a2 [1790] Improve Speed of Builds 2025-11-16 23:29:59 -05:00
David Sangrey ae10433f49 [1790] Improve Speed of Builds 2025-11-16 23:19:52 -05:00