diff --git a/ioc/tests/default.nix b/ioc/tests/default.nix index 7f7d020e..4150e7d8 100644 --- a/ioc/tests/default.nix +++ b/ioc/tests/default.nix @@ -4,6 +4,8 @@ with pkgs.lib; default-ioc-epics-base-3 = import ./default-ioc "3" args; default-ioc-epics-base-7 = import ./default-ioc "7" args; + pyepics = import ./pyepics args; + support-autosave-simple = import ./support/autosave/simple args; support-pvxs-ioc = import ./support/pvxs/ioc args; support-pvxs-qsrv2 = import ./support/pvxs/qsrv2 args; diff --git a/ioc/tests/pyepics/default.nix b/ioc/tests/pyepics/default.nix new file mode 100644 index 00000000..7c11aabc --- /dev/null +++ b/ioc/tests/pyepics/default.nix @@ -0,0 +1,47 @@ +{pkgs, ...}: let + inherit (pkgs) epnixLib lib; +in + pkgs.nixosTest { + name = "pytest"; + meta.maintainers = with epnixLib.maintainers; [minijackson]; + + extraPythonPackages = p: [p.pyepics]; + skipTypeCheck = true; + + nodes.ioc = { + imports = [ + (epnixLib.testing.softIoc '' + record(ai, "AI") { } + record(stringout, "STRINGOUT") { } + '') + ]; + }; + + testScript = let + python = lib.getExe (pkgs.python3.withPackages (p: [p.pyepics])); + iocTestScript = pkgs.writeText "iocTestScript.py" '' + import os + + import epics + + os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "NO" + os.environ["EPICS_CA_ADDR_LIST"] = "localhost" + + stringout = epics.PV("STRINGOUT") + + assert epics.caget("AI") == 0 + assert stringout.get() == "" + + assert epics.caput("AI", 42.0, wait=True) == 1 + assert stringout.put("hello", wait=True) == 1 + + assert epics.caget("AI") == 42 + assert stringout.get() == "hello" + ''; + in '' + start_all() + ioc.wait_for_unit("ioc.service") + + print(ioc.succeed("${python} ${iocTestScript}")) + ''; + } diff --git a/pkgs/default.nix b/pkgs/default.nix index 5330dbe3..d2c1817b 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -9,10 +9,15 @@ in mkEpicsPackage = callPackage ./build-support/mk-epics-package.nix {}; - python3Packages = prev.python3Packages.overrideScope (final: prev: { - lewis = final.callPackage ./epnix/tools/lewis {}; - scanf = final.callPackage ./epnix/tools/scanf {}; - }); + pythonPackagesExtensions = + prev.pythonPackagesExtensions + ++ [ + (final: prev: { + lewis = final.callPackage ./epnix/tools/lewis {}; + pyepics = final.callPackage ./epnix/python-modules/pyepics {}; + scanf = final.callPackage ./epnix/tools/scanf {}; + }) + ]; epnix = recurseExtensible (self: { # EPICS base @@ -57,7 +62,7 @@ in ca-gateway = callPackage ./epnix/tools/ca-gateway {}; - inherit (final.python3Packages) lewis; + inherit (final.python3Packages) lewis pyepics; inherit (callPackage ./epnix/tools/lewis/lib.nix {}) mkLewisSimulator; pcas = callPackage ./epnix/tools/pcas {}; diff --git a/pkgs/epnix/python-modules/pyepics/default.nix b/pkgs/epnix/python-modules/pyepics/default.nix new file mode 100644 index 00000000..644a4978 --- /dev/null +++ b/pkgs/epnix/python-modules/pyepics/default.nix @@ -0,0 +1,64 @@ +{ + stdenv, + epnix, + epnixLib, + buildPythonPackage, + fetchPypi, + setuptools, + setuptools-scm, + pyparsing, + numpy, + importlib-resources, +}: +buildPythonPackage rec { + pname = "pyepics"; + version = "3.5.6"; + format = "pyproject"; + + src = fetchPypi { + inherit pname version; + hash = "sha256-NYj8EVZHTgZR13DZ46ggXRFkqBROJI/CzaNghB7OCwc="; + }; + + nativeBuildInputs = [ + setuptools + setuptools-scm + ]; + + buildInputs = [epnix.epics-base]; + + propagatedBuildInputs = [ + pyparsing + numpy + importlib-resources + ]; + + postInstall = let + # TODO: this only works for x86_64-linux + inherit (stdenv) hostPlatform; + kernel = hostPlatform.parsed.kernel.name; + arch = + if hostPlatform.isx86 + then "" + else hostPlatform.parsed.cpu.family; + bits = toString hostPlatform.parsed.cpu.bits; + system = "${kernel}${arch}${bits}"; + + epicsSystem = epnixLib.toEpicsArch hostPlatform; + in '' + clibsDir=($out/lib/python*/site-packages/epics/clibs) + rm -rf $clibsDir/*/ + mkdir $clibsDir/${system} + # No need to copy libCom, since libca depend on it + ln -st $clibsDir/${system} ${epnix.epics-base}/lib/${epicsSystem}/libca.so + ''; + + pythonImportsCheck = ["epics"]; + + meta = { + description = "Python interface to Epics Channel Access"; + homepage = "https://github.com/pyepics/pyepics"; + license = epnixLib.licenses.epics; + maintainers = with epnixLib.maintainers; [minijackson]; + }; +}