Skip to content

Commit

Permalink
Add installer for standalone objdictedit (#42)
Browse files Browse the repository at this point in the history
* Add installer for standalone objdictedit
* Change objdictedit title
* Update tests
  • Loading branch information
sveinse committed Sep 11, 2024
1 parent 8ccdc10 commit 227aaf7
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 5 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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 @@ -197,9 +197,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 @@ -229,6 +229,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 @@ -323,9 +325,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

0 comments on commit 227aaf7

Please sign in to comment.