Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add installer for standalone objdictedit #42

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ This tool is a fork from upstream canfestival-3-asc repo:
> https://github.com/Laerdal/canfestival-3-asc


## Making objdictedit excutable

To be able build an executable that can be run from anywhere:

$ pip install pyinstaller
$ pyinstaller packaging/objdictedit.spec

The file `dist/objdictedit.exe` can now be used anywhere. It does not need any
pre-installed software.


## License

Objdictgen has been based on the python tool included in CanFestival. This
Expand Down
56 changes: 56 additions & 0 deletions packaging/filereplacer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

import os
import re
import warnings
import objdictgen
from setuptools import SetuptoolsDeprecationWarning
from setuptools.config import read_configuration

warnings.filterwarnings("ignore", category=SetuptoolsDeprecationWarning)


def convert(infile, outfile):
''' Tool to replace @@{VAR_NAME} in files.'''

pat = re.compile(r'^(.*?)@@{(.[^}]+)}(.*)$', re.S)

config = read_configuration('setup.cfg')["metadata"]

# Some hacks
config['version_tuple'] = objdictgen.__version_tuple__
config['copyright'] = objdictgen.__copyright__

with open(infile, "r", encoding="utf-8") as fin:
out = ''
for line in fin:

while True:
m = pat.fullmatch(line)
if not m:
out += line
break

out += m[1]
name = m[2]
line = m[3] # Remainder for the next iteration
if name in config:
out += str(config[m[2]])

elif name in os.environ:
out += os.environ[name]

else:
raise KeyError(f"The variable {name} is not defined")

with open(outfile, 'w', encoding="utf-8") as fout:
fout.write(out)


if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input', help='Input file')
parser.add_argument('output', help='Output file')
args = parser.parse_args()

convert(args.input, args.output)
63 changes: 63 additions & 0 deletions packaging/objdictedit.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# -*- mode: python ; coding: utf-8 -*-
import os
import sys
sys.path.append("packaging")
from filereplacer import convert

basepath = os.path.dirname(os.path.dirname(os.path.abspath(SPEC)))

workpath = basepath
os.chdir(workpath)

script = """
from objdictgen.ui import objdictedit
objdictedit.main()
"""

os.makedirs("build", exist_ok=True)
with open("build/objdictedit.py", "w", encoding="utf-8") as f:
f.write(script)

icon = basepath + "/src/objdictgen/img/networkedit.ico"

convert("packaging/objdictedit.ver.in", "build/objdictedit.ver")

a = Analysis(
[basepath + '/build/objdictedit.py'],
pathex=[],
binaries=[],
datas=[
(basepath + "/src/objdictgen/img/networkedit.ico", "objdictgen/img"),
],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='objdictedit',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=icon,
version=basepath + '/build/objdictedit.ver',
)
43 changes: 43 additions & 0 deletions packaging/objdictedit.ver.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=@@{version_tuple},
prodvers=@@{version_tuple},
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x4,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904B0',
[StringStruct(u'CompanyName', u'Object Dictionary Editor'),
StringStruct(u'FileDescription', u'@@{description}'),
StringStruct(u'FileVersion', u'@@{version}'),
StringStruct(u'InternalName', u'objdictedit'),
StringStruct(u'LegalCopyright', u'@@{copyright}'),
StringStruct(u'OriginalFilename', u'objdictedit.exe'),
StringStruct(u'ProductName', u'objdictedit'),
StringStruct(u'ProductVersion', u'@@{version}')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
sonar.projectKey=Laerdal_python-objdictgen
sonar.organization=laerdal-foss
sonar.python.coverage.reportPaths=coverage.xml
sonar.exclusions = tests/**
sonar.exclusions = tests/**, packaging/**
2 changes: 2 additions & 0 deletions src/objdictgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from objdictgen.nodemanager import NodeManager

__version__ = "3.5.1a1"
__version_tuple__ = (3, 5, 1, 1)
__copyright__ = "(c) 2024 Svein Seldal, Laerdal Medical AS, and several. Licensed under GPLv2.1."

# Shortcuts
LoadFile = Node.LoadFile
Expand Down
10 changes: 6 additions & 4 deletions src/objdictgen/ui/objdictedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ def _init_utils(self):

def _init_ctrls(self, parent):
wx.Frame.__init__(
self, id=ID_OBJDICTEDIT, name='objdictedit',
self, id=ID_OBJDICTEDIT, name=self.title,
parent=parent, pos=wx.Point(149, 178), size=wx.Size(1000, 700),
style=wx.DEFAULT_FRAME_STYLE, title='Objdictedit',
style=wx.DEFAULT_FRAME_STYLE, title=self.title,
)
self._init_utils()
self.SetClientSize(wx.Size(1000, 700))
Expand Down Expand Up @@ -228,6 +228,8 @@ def _init_ctrls(self, parent):
self.SetStatusBar(self.HelpBar)

def __init__(self, parent, manager: NodeManager|None = None, filesopen: list[TPath]|None = None):
self.title = f"Object dictionary editor v{objdictgen.__version__}"

filesopen = filesopen or []
if manager is None:
NodeEditorTemplate.__init__(self, NodeManager(), True)
Expand Down Expand Up @@ -322,9 +324,9 @@ def OnCloseFrame(self, event):

def RefreshTitle(self):
if self.FileOpened.GetPageCount() > 0:
self.SetTitle(f"Objdictedit - {self.Manager.GetCurrentFilename()}")
self.SetTitle(self.title + f" - {self.Manager.GetCurrentFilename()}")
else:
self.SetTitle("Objdictedit")
self.SetTitle(self.title)

def RefreshCurrentIndexList(self):
selected = self.FileOpened.GetSelection()
Expand Down
38 changes: 38 additions & 0 deletions tests/test_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

import pytest
from unittest import mock
import os
import sys

@pytest.fixture
def setenvvar(monkeypatch):
with mock.patch.dict(os.environ, {"TEST": "foobar"}):
yield


def test_filereplacer(basepath, wd, setenvvar):

sys.path.append(str(basepath / "packaging"))
os.chdir(basepath)
from filereplacer import convert

tests = [
(1, "Test data", "Test data"),
(2, "@@{name}", "objdictgen"),
(3, "@@{TEST}", "foobar"), # Read from the mocked environment variable
(4, "@@{nonexisting}", "non-existing"),
]

for i, data, result in tests:
infile = wd / "test.txt"
outfile = wd / "out.txt"
with open(infile, "w", encoding="utf-8") as f:
f.write(data)
if i == 4:
with pytest.raises(KeyError):
convert(infile, outfile)
continue
else:
convert(infile, outfile)
with open(outfile, "r", encoding="utf-8") as f:
assert f.read() == result