Skip to content

Commit

Permalink
feature(installer): update installer version and improve desktop vers…
Browse files Browse the repository at this point in the history
…ion launcher (#2157)

- updates installer submodule to benefit from last fixes and v2.18
handling
- make application load dependencies only when necessary, to
  speed up startup (in particular when only version is requested)
- make application exit when the server process ends, instead of hanging
- detect when server is already running
- separate one popup for "starting" and another for "started"
- fix alembic handling in pyinstaller:
  alembic must not be defined as a script, otherwise it's executed when
  the application quits. Instead, it must just be analyzed separately to get its
  dependencies

---------

Signed-off-by: Sylvain Leclerc <[email protected]>
  • Loading branch information
sylvlecl authored Oct 11, 2024
1 parent 172b7f8 commit f41b669
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 189 deletions.
27 changes: 17 additions & 10 deletions AntaresWebLinux.spec
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs

block_cipher = None

# We need to analyze all alembic files to be sure the migration phase works fine
migrations_dir = Path('alembic/versions')
migration_files = [str(f) for f in migrations_dir.iterdir() if f.is_file() and f.suffix == '.py']
# We need to analyze all alembic files to be sure the migration phase works fine:
# alembic loads version files by their path, so we need to add them as "data" to the package,
# but all the dependencies they use need to be included also, wo we need to perform a
# dedicated analyse for this.
versions_dir = Path('alembic/versions')
versions_files = [str(f) for f in versions_dir.iterdir() if f.is_file() and f.suffix == '.py']
alembic_analysis = Analysis(["alembic/env.py"] + versions_files)

binaries = [('./alembic.ini', './alembic.ini')] + collect_dynamic_libs('tables')

antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migration_files,
antares_web_server_a = Analysis(['antarest/gui.py'],
pathex=[],
binaries=binaries,
datas=[('./resources', './resources'), ('./alembic', './alembic')],
binaries=[],
datas=[('./resources', './resources'), ('./alembic', './alembic'), ('./alembic.ini', './')],
hiddenimports=[
'cmath',
'antarest.dbmodel',
'plyer.platforms.linux',
'plyer.platforms.linux.notification',
'pythonjsonlogger.jsonlogger',
'tables',
],
hookspath=['extra-hooks'],
hooksconfig={},
Expand All @@ -29,8 +31,13 @@ antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migratio
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
antares_web_server_pyz = PYZ(antares_web_server_a.pure, antares_web_server_a.zipped_data,

all_python = antares_web_server_a.pure + alembic_analysis.pure
all_zipped_data = antares_web_server_a.zipped_data + alembic_analysis.zipped_data

antares_web_server_pyz = PYZ(all_python, all_zipped_data,
cipher=block_cipher)

antares_web_server_exe = EXE(antares_web_server_pyz,
antares_web_server_a.scripts,
[],
Expand Down
27 changes: 17 additions & 10 deletions AntaresWebWin.spec
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
# -*- mode: python ; coding: utf-8 -*-
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs

block_cipher = None

# We need to analyze all alembic files to be sure the migration phase works fine
migrations_dir = Path('alembic/versions')
migration_files = [str(f) for f in migrations_dir.iterdir() if f.is_file() and f.suffix == '.py']
# We need to analyze all alembic files to be sure the migration phase works fine:
# alembic loads version files by their path, so we need to add them as "data" to the package,
# but all the dependencies they use need to be included also, wo we need to perform a
# dedicated analyse for this.
versions_dir = Path('alembic/versions')
versions_files = [str(f) for f in versions_dir.iterdir() if f.is_file() and f.suffix == '.py']
alembic_analysis = Analysis(["alembic/env.py"] + versions_files)

binaries = [('./alembic.ini', './alembic.ini')] + collect_dynamic_libs('tables')

antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migration_files,
antares_web_server_a = Analysis(['antarest/gui.py'],
pathex=[],
binaries=binaries,
datas=[('./resources', './resources'), ('./alembic', './alembic')],
binaries=[],
datas=[('./resources', './resources'), ('./alembic', './alembic'), ('./alembic.ini', './')],
hiddenimports=[
'cmath',
'antarest.dbmodel',
'plyer.platforms.win',
'plyer.platforms.win.notification',
'pythonjsonlogger.jsonlogger',
'tables',
],
hookspath=['extra-hooks'],
hooksconfig={},
Expand All @@ -29,8 +31,13 @@ antares_web_server_a = Analysis(['antarest/gui.py', 'alembic/env.py'] + migratio
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
antares_web_server_pyz = PYZ(antares_web_server_a.pure, antares_web_server_a.zipped_data,

all_python = antares_web_server_a.pure + alembic_analysis.pure
all_zipped_data = antares_web_server_a.zipped_data + alembic_analysis.zipped_data

antares_web_server_pyz = PYZ(all_python, all_zipped_data,
cipher=block_cipher)

antares_web_server_exe = EXE(antares_web_server_pyz,
antares_web_server_a.scripts,
[],
Expand Down
101 changes: 101 additions & 0 deletions antarest/core/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Copyright (c) 2024, RTE (https://www.rte-france.com)
#
# See AUTHORS.txt
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# SPDX-License-Identifier: MPL-2.0
#
# This file is part of the Antares project.

import argparse
from pathlib import Path


class PathType:
"""file or directory path type for `argparse` parser
The `PathType` class represents a type of argument that can be used
with the `argparse` library.
This class takes three boolean arguments, `exists`, `file_ok`, and `dir_ok`,
which specify whether the path argument must exist, whether it can be a file,
and whether it can be a directory, respectively.
Example Usage::
import argparse
from antarest.main import PathType
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=PathType(file_ok=True, exists=True))
args = parser.parse_args()
print(args.input)
In the above example, `PathType` is used to specify the type of the `--input`
argument for the `argparse` parser. The argument must be an existing file path.
If the given path is not an existing file, the argparse library raises an error.
The Path object representing the given path is then printed to the console.
"""

def __init__(
self,
exists: bool = False,
file_ok: bool = False,
dir_ok: bool = False,
) -> None:
if not (file_ok or dir_ok):
msg = "Either `file_ok` or `dir_ok` must be set at a minimum."
raise ValueError(msg)
self.exists = exists
self.file_ok = file_ok
self.dir_ok = dir_ok

def __call__(self, string: str) -> Path:
"""
Check whether the given string represents a valid path.
If `exists` is `False`, the method simply returns the given path.
If `exists` is True, it checks whether the path exists and whether it is
a file or a directory, depending on the values of `file_ok` and `dir_ok`.
If the path exists and is of the correct type, the method returns the path;
otherwise, it raises an :class:`argparse.ArgumentTypeError` with an
appropriate error message.
Args:
string: file or directory path
Returns:
the file or directory path
Raises
argparse.ArgumentTypeError: if the path is invalid
"""
file_path = Path(string).expanduser()
if not self.exists:
return file_path
if self.file_ok and self.dir_ok:
if file_path.exists():
return file_path
msg = f"The file or directory path does not exist: '{file_path}'"
raise argparse.ArgumentTypeError(msg)
elif self.file_ok:
if file_path.is_file():
return file_path
elif file_path.exists():
msg = f"The path is not a regular file: '{file_path}'"
else:
msg = f"The file path does not exist: '{file_path}'"
raise argparse.ArgumentTypeError(msg)
elif self.dir_ok:
if file_path.is_dir():
return file_path
elif file_path.exists():
msg = f"The path is not a directory: '{file_path}'"
else:
msg = f"The directory path does not exist: '{file_path}'"
raise argparse.ArgumentTypeError(msg)
else: # pragma: no cover
raise NotImplementedError((self.file_ok, self.dir_ok))
11 changes: 11 additions & 0 deletions antarest/desktop/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2024, RTE (https://www.rte-france.com)
#
# See AUTHORS.txt
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# SPDX-License-Identifier: MPL-2.0
#
# This file is part of the Antares project.
Loading

0 comments on commit f41b669

Please sign in to comment.