From 69cdf52660764dd6495662f1ecf5104745f992e6 Mon Sep 17 00:00:00 2001 From: Tom Cobb Date: Thu, 25 Feb 2021 14:20:09 +0000 Subject: [PATCH] Convert to use epicscorelibs --- .gitattributes | 1 + .gitignore | 20 +- .gitmodules | 3 + Pipfile | 22 ++ Pipfile.lock | 491 ++++++++++++++++++++++++++++++++++++++++ epicscorelibs | 1 + pyproject.toml | 3 + pythonIoc.in | 41 ---- setup.cfg | 50 ++++ setup.py | 95 ++++++++ softioc/__init__.py | 2 + softioc/__main__.py | 25 ++ softioc/_version_git.py | 97 ++++++++ softioc/builder.py | 16 +- softioc/device.py | 8 +- softioc/extension.c | 124 ++++++---- softioc/fields.py | 37 +-- softioc/imports.py | 88 ++----- softioc/iocStats | 1 + softioc/pvlog.py | 5 +- softioc/softioc.py | 6 +- tests/test_ioc.py | 4 +- 22 files changed, 938 insertions(+), 202 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitmodules create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 120000 epicscorelibs create mode 100644 pyproject.toml delete mode 100644 pythonIoc.in create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 softioc/__main__.py create mode 100644 softioc/_version_git.py create mode 160000 softioc/iocStats diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..41a13933 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*/_version_git.py export-subst diff --git a/.gitignore b/.gitignore index 390de251..185e73b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ -/bin -/dbd -/lib -/db -/docs/html -/pythonIoc +/.vscode +/venv *.pyc -O.* +/docs/html/ +/softioc/_extension.* + +# Dist build output +/build +/dist +/softioc.egg-info/ + +# Coverage reports +.coverage +cov.xml /docs/papers/*/*.aux /docs/papers/*/*.pdf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4c41b1de --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "iocStats"] + path = softioc/iocStats + url = https://github.com/epics-modules/iocStats.git diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..77850d53 --- /dev/null +++ b/Pipfile @@ -0,0 +1,22 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +pytest-cov = "*" +pytest-flake8 = "*" +sphinx-rtd-theme = "*" +# switch to main repo after PR https://github.com/Holzhaus/sphinx-multiversion/pull/60 is merged +sphinx-multiversion = {editable = true,git = "https://github.com/dls-controls/sphinx-multiversion.git",ref = "only-arg"} +setuptools-dso = "*" + +[packages] +# All other package requirements from setup.py +softioc = {editable = true,path = "."} + +[scripts] +# Put coverage here so we don't interfere with debugging in the IDE +tests = "python -m pytest --cov=epicsdbbuilder --cov-report term" +docs = "sphinx-build -EWT --keep-going docs build/html" +clean = "rm -rf build prefix */__pycache__ .coverage cov.xml *.egg-info .mypy_cache .pytest_cache" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 00000000..e9f0a69f --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,491 @@ +{ + "_meta": { + "hash": { + "sha256": "facfc60cd8deb78e8b7b88e3c72257aaa7c5faf1db8d612961fa1073173ef2dd" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "epicscorelibs": { + "hashes": [ + "sha256:1a131556d13775f820db2e474a117e47b0d743ef262f0ebb58207d0bd7a2c6be", + "sha256:1b235379d23bb2f5746a791cf4abc982d0252e84d8d6f1ce52f3d028cf1c02c2", + "sha256:25a1f8f6cf97bee3513bcc81f17eb6acbe8ceb6a9d00d2bb713989cf120f5f9a", + "sha256:304cf11a9b05b20ba925564ff55ac524a80365cb27c0cc8df7f902345893c783", + "sha256:3221c993009ffd893b13d136e4c31e3dcf2e7b34dc3321e583534185e521c0b5", + "sha256:328276f0aa4dcf367c3c96d0aa7bc7f89b845d7c3c5ac2bd0eacd960c880006c", + "sha256:37c0f6889cebd2d8138d457d4a884f96854e9b43eafc8deaf20161d6ca86f733", + "sha256:39bdfb7b931dceecb8dff3fb6ed9689aad542ff1b4f4f48fd89cb71bb565f14c", + "sha256:46a4e6f1c26eb460d56a55f93230185555cb7e9874859996fc8828515616673e", + "sha256:4dafa4f28ce858338bb04398da4a67014b3f6522c609ed9c3c9317ad59cc4045", + "sha256:55cdeaa11292db310273c1e408d09030c1a71b27237818ea2cd22b6e03859ba6", + "sha256:56ba9513d9e44b19992e4a7e975c059b669d04f705e72e2967cc71c02c5b7a5b", + "sha256:5769b8aea4427513c3fd9e8dd08612bdaa94e2ee4975ae9436a8cb6f633e3ecb", + "sha256:5a72ef5e9aa2a467dbc31fb1a6bfcac44eeb2d50802991061a130773565a67f0", + "sha256:5f165e984c123164d58afeb0d8ca8cfddb20bc3603d21837f7232afc1cff0471", + "sha256:6b8e63342c2d0778d81735fd7bba0467778ddf0d2997a7ee30308b0322ed7e88", + "sha256:6e111d499db1a496bf3331db781894d2da301b1439e32aa464bfd30bf1bae192", + "sha256:8afa93e18d26e25168cda51e5aa72054c35d50029f7057d5ba3c9108fdb2751f", + "sha256:9b4be973536644aab49e67a42c41f4922f32b7106cde8f360b9ffe198f971c1a", + "sha256:a28f9c45b2c4a273ac9735245145b80247908c9aceab3f01550756023d1c61fd", + "sha256:a5e4bc0722c48e872f0fc5752b514fe9167ab1dce6cfdad79687f8d2a130d0c1", + "sha256:db59c3bf4fda65eb49ad5ab4933aa182c764200c8ed57d8e5474d7accbbcb609", + "sha256:e6dd41d204565bccc7126eb43a19187c6ff4a2c828bf2a79161cb4cef19cbef9", + "sha256:f7360c05368df5036d87b48a8b97c1d45970dfe4fca9f853b862a0609f2cf093", + "sha256:fa30a31d60173a83c185caf9e77c2666f3f20dd9b7c6ffd3aa49ef511199f258" + ], + "markers": "python_version >= '2.7'", + "version": "==7.0.4.99.0.0a1" + }, + "epicsdbbuilder": { + "hashes": [ + "sha256:0c3bb0ae55d618474880c1004218a3d3ac640235d04426df6622b75229c563fc", + "sha256:be4c37aead8f2bb5500ef3e7693eee97ecd4fb3c1090324689062d0505de3325" + ], + "version": "==1.4" + }, + "numpy": { + "hashes": [ + "sha256:032be656d89bbf786d743fee11d01ef318b0781281241997558fa7950028dd29", + "sha256:104f5e90b143dbf298361a99ac1af4cf59131218a045ebf4ee5990b83cff5fab", + "sha256:125a0e10ddd99a874fd357bfa1b636cd58deb78ba4a30b5ddb09f645c3512e04", + "sha256:12e4ba5c6420917571f1a5becc9338abbde71dd811ce40b37ba62dec7b39af6d", + "sha256:13adf545732bb23a796914fe5f891a12bd74cf3d2986eed7b7eba2941eea1590", + "sha256:2d7e27442599104ee08f4faed56bb87c55f8b10a5494ac2ead5c98a4b289e61f", + "sha256:3bc63486a870294683980d76ec1e3efc786295ae00128f9ea38e2c6e74d5a60a", + "sha256:3d3087e24e354c18fb35c454026af3ed8997cfd4997765266897c68d724e4845", + "sha256:4ed8e96dc146e12c1c5cdd6fb9fd0757f2ba66048bf94c5126b7efebd12d0090", + "sha256:60759ab15c94dd0e1ed88241fd4fa3312db4e91d2c8f5a2d4cf3863fad83d65b", + "sha256:65410c7f4398a0047eea5cca9b74009ea61178efd78d1be9847fac1d6716ec1e", + "sha256:66b467adfcf628f66ea4ac6430ded0614f5cc06ba530d09571ea404789064adc", + "sha256:7199109fa46277be503393be9250b983f325880766f847885607d9b13848f257", + "sha256:72251e43ac426ff98ea802a931922c79b8d7596480300eb9f1b1e45e0543571e", + "sha256:89e5336f2bec0c726ac7e7cdae181b325a9c0ee24e604704ed830d241c5e47ff", + "sha256:89f937b13b8dd17b0099c7c2e22066883c86ca1575a975f754babc8fbf8d69a9", + "sha256:9c94cab5054bad82a70b2e77741271790304651d584e2cdfe2041488e753863b", + "sha256:9eb551d122fadca7774b97db8a112b77231dcccda8e91a5bc99e79890797175e", + "sha256:a1d7995d1023335e67fb070b2fae6f5968f5be3802b15ad6d79d81ecaa014fe0", + "sha256:ae61f02b84a0211abb56462a3b6cd1e7ec39d466d3160eb4e1da8bf6717cdbeb", + "sha256:b9410c0b6fed4a22554f072a86c361e417f0258838957b78bd063bde2c7f841f", + "sha256:c26287dfc888cf1e65181f39ea75e11f42ffc4f4529e5bd19add57ad458996e2", + "sha256:c91ec9569facd4757ade0888371eced2ecf49e7982ce5634cc2cf4e7331a4b14", + "sha256:ecb5b74c702358cdc21268ff4c37f7466357871f53a30e6f84c686952bef16a9" + ], + "markers": "python_version >= '3.7'", + "version": "==1.20.1" + }, + "softioc": { + "editable": true, + "path": "." + } + }, + "develop": { + "alabaster": { + "hashes": [ + "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", + "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" + ], + "version": "==0.7.12" + }, + "attrs": { + "hashes": [ + "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", + "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==20.3.0" + }, + "babel": { + "hashes": [ + "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5", + "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.9.0" + }, + "certifi": { + "hashes": [ + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + ], + "version": "==2020.12.5" + }, + "chardet": { + "hashes": [ + "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", + "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==4.0.0" + }, + "coverage": { + "hashes": [ + "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7", + "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5", + "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f", + "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde", + "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f", + "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f", + "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c", + "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66", + "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90", + "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337", + "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d", + "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4", + "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409", + "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37", + "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1", + "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247", + "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39", + "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c", + "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994", + "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c", + "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb", + "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc", + "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f", + "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca", + "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135", + "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3", + "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339", + "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9", + "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9", + "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af", + "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370", + "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19", + "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3", + "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44", + "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3", + "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a", + "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c", + "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b", + "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9", + "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8", + "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22", + "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f", + "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345", + "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880", + "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0", + "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b", + "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec", + "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3", + "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==5.4" + }, + "docutils": { + "hashes": [ + "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", + "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.16" + }, + "flake8": { + "hashes": [ + "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", + "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.8.4" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, + "imagesize": { + "hashes": [ + "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", + "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.2.0" + }, + "iniconfig": { + "hashes": [ + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" + ], + "version": "==1.1.1" + }, + "jinja2": { + "hashes": [ + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==2.11.3" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.1.1" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "packaging": { + "hashes": [ + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==20.9" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.13.1" + }, + "py": { + "hashes": [ + "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", + "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.10.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", + "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.6.0" + }, + "pyflakes": { + "hashes": [ + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.2.0" + }, + "pygments": { + "hashes": [ + "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0", + "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88" + ], + "markers": "python_version >= '3.5'", + "version": "==2.8.0" + }, + "pyparsing": { + "hashes": [ + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==2.4.7" + }, + "pytest": { + "hashes": [ + "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9", + "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839" + ], + "markers": "python_version >= '3.6'", + "version": "==6.2.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7", + "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da" + ], + "index": "pypi", + "version": "==2.11.1" + }, + "pytest-flake8": { + "hashes": [ + "sha256:c28cf23e7d359753c896745fd4ba859495d02e16c84bac36caa8b1eec58f5bc1", + "sha256:f0259761a903563f33d6f099914afef339c085085e643bee8343eb323b32dd6b" + ], + "index": "pypi", + "version": "==1.0.7" + }, + "pytz": { + "hashes": [ + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" + ], + "version": "==2021.1" + }, + "requests": { + "hashes": [ + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==2.25.1" + }, + "setuptools-dso": { + "hashes": [ + "sha256:93a2faddf76b447c0aa58d42ccb22dbc7d6ce46d6e0b32dfacc143c6acdc016c" + ], + "index": "pypi", + "version": "==1.7" + }, + "snowballstemmer": { + "hashes": [ + "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2", + "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914" + ], + "version": "==2.1.0" + }, + "sphinx": { + "hashes": [ + "sha256:11d521e787d9372c289472513d807277caafb1684b33eb4f08f7574c405893a9", + "sha256:e90161222e4d80ce5fc811ace7c6787a226b4f5951545f7f42acf97277bfc35c" + ], + "markers": "python_version >= '3.5'", + "version": "==3.5.1" + }, + "sphinx-multiversion": { + "editable": true, + "git": "https://github.com/dls-controls/sphinx-multiversion.git", + "ref": "3e72beb7f8f0a76b53722fb2eb3daeed3d2a3d31" + }, + "sphinx-rtd-theme": { + "hashes": [ + "sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5", + "sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113" + ], + "index": "pypi", + "version": "==0.5.1" + }, + "sphinxcontrib-applehelp": { + "hashes": [ + "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a", + "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.2" + }, + "sphinxcontrib-devhelp": { + "hashes": [ + "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", + "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.2" + }, + "sphinxcontrib-htmlhelp": { + "hashes": [ + "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", + "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.3" + }, + "sphinxcontrib-jsmath": { + "hashes": [ + "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", + "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.1" + }, + "sphinxcontrib-qthelp": { + "hashes": [ + "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", + "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.3" + }, + "sphinxcontrib-serializinghtml": { + "hashes": [ + "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", + "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" + ], + "markers": "python_version >= '3.5'", + "version": "==1.1.4" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==0.10.2" + }, + "urllib3": { + "hashes": [ + "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", + "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.3" + } + } +} diff --git a/epicscorelibs b/epicscorelibs new file mode 120000 index 00000000..35e3e70a --- /dev/null +++ b/epicscorelibs @@ -0,0 +1 @@ +/home/tom/.local/share/virtualenvs/pythonIoc-_YBBu27A/lib/python3.8/site-packages/epicscorelibs \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6536485d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_dso", "epicscorelibs>=7.0.4.99.0.0a1"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/pythonIoc.in b/pythonIoc.in deleted file mode 100644 index d0a3ac31..00000000 --- a/pythonIoc.in +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# Wrapper for invoking the Python softIoc. This IOC needs to be called with a -# complete path and with PYTHONPATH set up correctly. It's easier to -# configure this here in shell script than in the program itself! - -export HERE="$(readlink -fn "$(dirname "$0")")" -export PYTHONPATH="$PYTHONPATH${PYTHONPATH:+:}$HERE/python" -export EPICS_BASE='@@EPICS_BASE@@' -export EPICS_HOST_ARCH='@@EPICS_HOST_ARCH@@' -export PYTHONHOME='@@PYTHONHOME@@' - -case "$1" in - --debug) - # Run under gdb - shift - TEMP="$(mktemp)" - trap 'rm -f "$TEMP"' EXIT - echo run "$@" >>"$TEMP" - gdb -x "$TEMP" "$HERE/bin/$EPICS_HOST_ARCH/softIoc" - ;; - - --valgrind) - # Run under valgrind with Python suppression file. - shift - PYTHON_SRC=/dls_sw/prod/tools/RHEL5/src/Python-2.6.4 - SUPP="$PYTHON_SRC"/Misc/valgrind-python.supp - TEMP=$(mktemp) - trap 'rm -f "$TEMP"' EXIT - # Hack up the suppression file. Unfortunately this only works with the - # one particular version of the file (line numbers!) - sed '127,161{/^###/s///}' "$SUPP" >"$TEMP" - valgrind --tool=memcheck --suppressions="$TEMP" \ - "$HERE/bin/$EPICS_HOST_ARCH/softIoc" "$@" - ;; - - *) - # Normal operation - exec "$HERE/bin/$EPICS_HOST_ARCH/softIoc" "$@" - ;; -esac diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..d906dcc8 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,50 @@ +[metadata] +name = softioc +description = EPICS IOC with Python Device Support +url = https://github.com/dls-controls/pythonIoc +author = Michael Abbott +author_email = Michael.Abbott@diamond.ac.uk +license = Apache License 2.0 +long_description = file: README.rst +long_description_content_type = text/x-rst +classifiers = + Development Status :: 5 - Production/Stable + License :: OSI Approved :: Apache Software License + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + +[options] +packages = find: + +[options.packages.find] +# Don't include our test directory in the distribution +exclude = tests + +[options.entry_points] +# Include a command line script +console_scripts = + pythonIoc = softioc.__main__:main + +[options.package_data] +softioc = + softioc/access.acf + softioc/device.dbd + softioc/iocStats/devIocStats/src/devIocStats.dbd + softioc/iocStats/iocAdmin/Db/*.template + +[flake8] +max-line-length = 80 +extend-ignore = + F401 F403 F405 # Allow from module import * + E251 # Allow call(param = value) + E301 E302 E303 E305 # Allow any number of blank lines + +[tool:pytest] +# Run pytest with all our checkers, and don't spam us with massive tracebacks on error +addopts = --tb=native -vv --flake8 --doctest-modules + +[coverage:run] +# This is covered in the versiongit test suite so exclude it here +omit = */_version_git.py \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..baad602a --- /dev/null +++ b/setup.py @@ -0,0 +1,95 @@ +import os +import sys + +from setuptools.command.develop import develop +import epicscorelibs.path +import epicscorelibs.version +from setuptools_dso import DSO, Extension, setup +from epicscorelibs.config import get_config_var + +# Place the directory containing _version_git on the path +for path, _, filenames in os.walk(os.path.dirname(os.path.abspath(__file__))): + if "_version_git.py" in filenames: + sys.path.append(path) + break + +from _version_git import __version__, get_cmdclass # noqa + +devIocStats_OSI = [ + "devIocStatsAnalog.c", + "devIocStatsString.c", + "devIocStatsWaveform.c", + "devIocStatsSub.c", + "devIocStatsTest.c" +] + +devIocStats_OSD = [ + "osdCpuUsage.c", + "osdCpuUtilization.c", + "osdFdUsage.c", + "osdMemUsage.c", + "osdWorkspaceUsage.c", + "osdClustInfo.c", + "osdSuspTasks.c", + "osdIFErrors.c", + "osdBootInfo.c", + "osdSystemInfo.c", + "osdHostInfo.c", + "osdPIDInfo.c", +] + +devIocStats_srcs = [] +devIocStats_src = os.path.join("softioc", "iocStats", "devIocStats") +devIocStats_default = os.path.join(devIocStats_src, "os", "default") +devIocStats_os = os.path.join(devIocStats_src, "os", get_config_var('OS_CLASS')) + +for f in devIocStats_OSI: + devIocStats_srcs.append(os.path.join(devIocStats_src, f)) +for f in devIocStats_OSD: + if os.path.exists(os.path.join(devIocStats_os, f)): + devIocStats_srcs.append(os.path.join(devIocStats_os, f)) + else: + devIocStats_srcs.append(os.path.join(devIocStats_default, f)) + +#dso = DSO( +# 'softioc.lib.devIocStats', +# devIocStats_srcs, +# include_dirs=[epicscorelibs.path.include_path, devIocStats_src, devIocStats_os, devIocStats_default], +# dsos=['epicscorelibs.lib.Com'] +#) + +# Extension with all our C code +ext = Extension( + name='softioc._extension', + sources = ['softioc/extension.c'] + devIocStats_srcs, + include_dirs=[epicscorelibs.path.include_path, devIocStats_src, devIocStats_os, devIocStats_default], + dsos = ['epicscorelibs.lib.dbCore', 'epicscorelibs.lib.Com'], + define_macros = get_config_var('CPPFLAGS'), + extra_compile_args = get_config_var('CXXFLAGS'), + extra_link_args = get_config_var('LDFLAGS'), +) + +# Add custom develop to add soft link to epicscorelibs in . +class Develop(develop): + def install_for_development(self): + develop.install_for_development(self) + # Make a link here to epicscorelibs so `pip install -e .` works + # If we don't do this dbCore can't be found when _extension is + # built into . + link = os.path.join(self.egg_path, "epicscorelibs") + if not os.path.exists(link): + os.symlink(os.path.join(self.install_dir, "epicscorelibs"), link) + +setup( + cmdclass=dict(develop=Develop, **get_cmdclass()), + version=__version__, + ext_modules = [ext], + install_requires = [ + "epicscorelibs==7.0.4.99.0.0a1", + #epicscorelibs.version.abi_requires(), + "numpy>=1.18", + "epicsdbbuilder>=1.4" + ], + #x_dsos = [dso], + zip_safe = False, # setuptools_dso is not compatible with eggs! +) diff --git a/softioc/__init__.py b/softioc/__init__.py index 7b9a7500..bb8d9bf2 100644 --- a/softioc/__init__.py +++ b/softioc/__init__.py @@ -1 +1,3 @@ '''Python soft IOC module.''' + +from ._version_git import __version__ diff --git a/softioc/__main__.py b/softioc/__main__.py new file mode 100644 index 00000000..e15de04a --- /dev/null +++ b/softioc/__main__.py @@ -0,0 +1,25 @@ +import os +import sys +from argparse import ArgumentParser + +from softioc import __version__ + + +def main(args=None): + parser = ArgumentParser() + parser.add_argument("--version", action="version", version=__version__) + parser.add_argument("script", help="The python script to run") + parsed_args = parser.parse_args(args) + # Insert the directory containing script onto the path in case we do + # any imports + sys.path.insert(0, os.path.dirname(os.path.abspath(parsed_args.script))) + if sys.version_info < (3, 0): + # Python 2 + execfile(parsed_args.script) + else: + # Python 3 + exec(open(parsed_args.script).read()) + + +if __name__ == "__main__": + main() diff --git a/softioc/_version_git.py b/softioc/_version_git.py new file mode 100644 index 00000000..ec811d1b --- /dev/null +++ b/softioc/_version_git.py @@ -0,0 +1,97 @@ +# Compute a version number from a git repo or archive + +# This file is released into the public domain. Generated by: +# versiongit-1.0 (https://github.com/dls-controls/versiongit) +import os +import re +import sys +from subprocess import STDOUT, CalledProcessError, check_output + +# These will be filled in if git archive is run or by setup.py cmdclasses +GIT_REFS = "$Format:%D$" +GIT_SHA1 = "$Format:%h$" + +# Git describe gives us sha1, last version-like tag, and commits since then +CMD = "git describe --tags --dirty --always --long --match=[0-9]*[-.][0-9]*" + + +def get_version_from_git(path=None): + """Try to parse version from git describe, fallback to git archive tags""" + tag, plus, suffix = "0.0", "untagged", "" + if not GIT_SHA1.startswith("$"): + # git archive or the cmdclasses below have filled in these strings + sha1 = GIT_SHA1 + for ref_name in GIT_REFS.split(", "): + if ref_name.startswith("tag: "): + # git from 1.8.3 onwards labels archive tags "tag: TAGNAME" + tag, plus = ref_name[5:], "0" + else: + if path is None: + # If no path to git repo, choose the directory this file is in + path = os.path.dirname(os.path.abspath(__file__)) + # output is TAG-NUM-gHEX[-dirty] or HEX[-dirty] + try: + cmd_out = check_output(CMD.split(), stderr=STDOUT, cwd=path) + except Exception as e: + sys.stderr.write("%s: %s\n" % (type(e).__name__, str(e))) + if isinstance(e, CalledProcessError): + sys.stderr.write("-> %s" % e.output.decode()) + return "0.0+unknown", None, e + else: + out = cmd_out.decode().strip() + if out.endswith("-dirty"): + out = out[:-6] + suffix = ".dirty" + if "-" in out: + # There is a tag, extract it and the other pieces + match = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", out) + tag, plus, sha1 = match.groups() + else: + # No tag, just sha1 + sha1 = out + # Replace dashes in tag for dots + tag = tag.replace("-", ".") + if plus != "0" or suffix: + # Not on a tag, add additional info + tag = "%(tag)s+%(plus)s.g%(sha1)s%(suffix)s" % locals() + return tag, sha1, None + + +__version__, git_sha1, git_error = get_version_from_git() + + +def get_cmdclass(build_py=None, sdist=None): + """Create cmdclass dict to pass to setuptools.setup that will write a + _version_static.py file in our resultant sdist, wheel or egg""" + if build_py is None: + from setuptools.command.build_py import build_py + if sdist is None: + from setuptools.command.sdist import sdist + + def make_version_static(base_dir, pkg): + vg = os.path.join(base_dir, pkg.split(".")[0], "_version_git.py") + if os.path.isfile(vg): + lines = open(vg).readlines() + with open(vg, "w") as f: + for line in lines: + # Replace GIT_* with static versions + if line.startswith("GIT_SHA1 = "): + f.write("GIT_SHA1 = '%s'\n" % git_sha1) + elif line.startswith("GIT_REFS = "): + f.write("GIT_REFS = 'tag: %s'\n" % __version__) + else: + f.write(line) + + class BuildPy(build_py): + def run(self): + build_py.run(self) + for pkg in self.packages: + make_version_static(self.build_lib, pkg) + + class Sdist(sdist): + def make_release_tree(self, base_dir, files): + sdist.make_release_tree(self, base_dir, files) + for pkg in self.distribution.packages: + make_version_static(base_dir, pkg) + + return dict(build_py=BuildPy, sdist=Sdist) diff --git a/softioc/builder.py b/softioc/builder.py index 48fc1cd1..9aee2a8d 100644 --- a/softioc/builder.py +++ b/softioc/builder.py @@ -5,8 +5,20 @@ from .softioc import dbLoadDatabase from epicsdbbuilder import * -InitialiseDbd(os.environ['EPICS_BASE'], os.environ['EPICS_HOST_ARCH']) -LoadDbdFile(os.path.join(os.environ['HERE'], 'dbd/device.dbd')) +from epicscorelibs import path +from epicscorelibs.ioc import iocshRegisterCommon, registerRecordDeviceDriver, pdbbase + +iocshRegisterCommon() +dbLoadDatabase("base.dbd", os.path.join(path.base_path, "dbd")) + +dbd_dir = os.path.join(os.path.dirname(__file__), "iocStats", "devIocStats") +dbLoadDatabase("devIocStats.dbd", dbd_dir) + +if registerRecordDeviceDriver(pdbbase): + raise RuntimeError('Error registering') + +InitialiseDbd() +LoadDbdFile(os.path.join(os.path.dirname(__file__), 'device.dbd')) from . import pythonSoftIoc PythonDevice = pythonSoftIoc.PythonDevice() diff --git a/softioc/device.py b/softioc/device.py index db044108..d3f1d4ef 100644 --- a/softioc/device.py +++ b/softioc/device.py @@ -8,11 +8,9 @@ from . import alarm from .fields import DbfCodeToNumpy, DbrToDbfCode -from .imports import dbLoadDatabase, recGblResetAlarms +from .imports import dbLoadDatabase, recGblResetAlarms, db_put_field from .device_core import DeviceSupportCore, RecordLookup -from . import imports - class ProcessDeviceSupportCore(DeviceSupportCore, RecordLookup): '''Implements canonical default processing for records with a _process @@ -204,7 +202,7 @@ def set(self, value, process=True): else: datatype, length, data, array = self.value_to_dbr(value) self.__enable_write = process - imports.db_put_field( + db_put_field( _record.NAME, DbrToDbfCode[datatype], data, length) self.__enable_write = True @@ -352,4 +350,4 @@ class waveform_out(WaveformBase, ProcessDeviceSupportOut): # Ensure the .dbd file is loaded. -dbLoadDatabase(os.path.join(os.environ['HERE'], 'dbd/device.dbd'), None, None) +dbLoadDatabase("device.dbd", os.path.dirname(__file__), None) diff --git a/softioc/extension.c b/softioc/extension.c index 7a3dfb37..cbeb44c4 100644 --- a/softioc/extension.c +++ b/softioc/extension.c @@ -1,6 +1,5 @@ -/* See note in softMain.c about these #undefs. */ -#undef _POSIX_C_SOURCE -#undef _XOPEN_SOURCE + +#define PY_SSIZE_T_CLEAN #include #include @@ -10,23 +9,9 @@ #include #include #include - - -/* The interface to the caput event callback has changed as of EPICS 3.15, and - * we need to compile as appropriate. */ -#define BASE_3_15 (EPICS_VERSION * 100 + EPICS_REVISION >= 315) -#if BASE_3_15 #include -#endif - - - -/* Returns the EPICS_BASE path used to build this IOC. */ -const char *get_EPICS_BASE(void) -{ - return EPICS_BASE; // Passed as #define from Makefile -} - +#include +#include /* In Python3 this function has been renamed. */ #if PY_MAJOR_VERSION >= 3 @@ -40,9 +25,8 @@ const char *get_EPICS_BASE(void) /* Alas, EPICS has changed the numerical assignments of the DBF_ enums between * versions, so to avoid unpleasant surprises, we compute thes values here in C * and pass them back to the Python layer. */ -PyObject *get_DBF_values(void) +static PyObject *get_DBF_values(PyObject *self, PyObject *args) { - PyGILState_STATE gstate = PyGILState_Ensure(); PyObject *dict = PyDict_New(); ADD_ENUM(dict, DBF_STRING); ADD_ENUM(dict, DBF_CHAR); @@ -60,7 +44,6 @@ PyObject *get_DBF_values(void) ADD_ENUM(dict, DBF_OUTLINK); ADD_ENUM(dict, DBF_FWDLINK); ADD_ENUM(dict, DBF_NOACCESS); - PyGILState_Release(gstate); return dict; } @@ -68,11 +51,15 @@ PyObject *get_DBF_values(void) /* Given an array of field names, this routine looks up each field name in * the EPICS database and returns the corresponding field offset. */ -void get_field_offsets( - const char * record_type, const char * field_names[], int field_count, - short field_offset[], short field_size[], short field_type[]) +static PyObject *get_field_offsets(PyObject *self, PyObject *args) { int status; + const char *record_type; + PyObject *dict = PyDict_New(); + + if (!PyArg_ParseTuple(args, "s", &record_type)) + return NULL; + DBENTRY dbentry; dbInitEntry(pdbbase, &dbentry); @@ -85,33 +72,36 @@ void get_field_offsets( while (status == 0) { const char * field_name = dbGetFieldName(&dbentry); - int i; - for (i = 0; i < field_count; i ++) - { - if (strcmp(field_names[i], field_name) == 0) - { - field_offset[i] = dbentry.pflddes->offset; - field_size[i] = dbentry.pflddes->size; - field_type[i] = dbentry.pflddes->field_type; - } - } + PyObject *ost = Py_BuildValue("iii", + dbentry.pflddes->offset, + dbentry.pflddes->size, + dbentry.pflddes->field_type); + PyDict_SetItemString(dict, field_name, ost); status = dbNextField(&dbentry, 0); } dbFinishEntry(&dbentry); + return dict; } /* Updates PV field with integrated db lookup. Safer to do this in C as we need * an intermediate copy of the dbAddr structure, which changes size between * EPICS releases. */ -int db_put_field(const char *name, short dbrType, void *pbuffer, long length) +static PyObject *db_put_field(PyObject *self, PyObject *args) { + const char *name; + short dbrType; + void *pbuffer; + long length; + if (!PyArg_ParseTuple(args, "shnl", &name, &dbrType, &pbuffer, &length)) + return NULL; + struct dbAddr dbAddr; int rc = dbNameToAddr(name, &dbAddr); if (rc == 0) rc = dbPutField(&dbAddr, dbrType, pbuffer, length); - return rc; + return Py_BuildValue("i", rc); } @@ -178,12 +168,8 @@ static void PrintValue(struct formatted *formatted) void EpicsPvPutHook(struct asTrapWriteMessage *pmessage, int after) { -#if BASE_3_15 struct dbChannel *pchan = pmessage->serverSpecific; dbAddr *dbaddr = &pchan->addr; -#else - dbAddr *dbaddr = pmessage->serverSpecific; -#endif struct formatted *value = FormatValue(dbaddr); if (after) @@ -205,3 +191,59 @@ void EpicsPvPutHook(struct asTrapWriteMessage *pmessage, int after) /* Just save the old value for logging after. */ pmessage->userPvt = value; } + +static PyObject *install_pv_logging(PyObject *self, PyObject *args) +{ + const char *acf_file; + + if (!PyArg_ParseTuple(args, "s", &acf_file)) + return NULL; + + asSetFilename(acf_file); + asTrapWriteRegisterListener(EpicsPvPutHook); + Py_RETURN_NONE; +} + +static struct PyMethodDef softioc_methods[] = { + {"get_DBF_values", get_DBF_values, METH_VARARGS, + "Get a map of DBF names to values"}, + {"get_field_offsets", get_field_offsets, METH_VARARGS, + "Get offset, size and type for each record field"}, + {"db_put_field", db_put_field, METH_VARARGS, + "Put a database field to a value"}, + {"install_pv_logging", install_pv_logging, METH_VARARGS, + "Install caput logging to stdout"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef softioc_module = { + PyModuleDef_HEAD_INIT, + "softioc._extension", + NULL, + -1, + softioc_methods, +}; +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PyMOD(NAME) PyObject* PyInit_##NAME (void) +#else +# define PyMOD(NAME) void init##NAME (void) +#endif + +PyMOD(_extension) +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *mod = PyModule_Create(&softioc_module); +#else + PyObject *mod = Py_InitModule("softioc._extension", softioc_methods); +#endif + if(mod) { + } +#if PY_MAJOR_VERSION >= 3 + return mod; +#else + (void)mod; +#endif +} \ No newline at end of file diff --git a/softioc/fields.py b/softioc/fields.py index 6a598773..5a6dd74d 100644 --- a/softioc/fields.py +++ b/softioc/fields.py @@ -7,8 +7,8 @@ from .imports import get_field_offsets, get_DBF_values import numpy -from cothread.dbr import * -from cothread.dbr import ca_timestamp, EPICS_epoch +from epicscorelibs.ca.dbr import * +from epicscorelibs.ca.dbr import ca_timestamp, EPICS_epoch # Pick up the DBF_ definitions from the C helper layer. This is safer than @@ -82,35 +82,10 @@ class RecordFactory(object): def __init__(self, record_type, fields): '''Uses the EPICS static database to discover the offset in the record type and the size of each of the specified fields.''' - length = len(fields) - field_name_strings = [ - create_string_buffer(field.encode()) - for field in fields] - - field_names = (c_void_p * len(field_name_strings))() - for i, field in enumerate(field_name_strings): - field_names[i] = addressof(field) - - field_offsets = numpy.empty(length, dtype = numpy.int16) - field_sizes = numpy.zeros(length, dtype = numpy.int16) - field_types = numpy.empty(length, dtype = numpy.int16) - - get_field_offsets( - record_type, field_names, length, - field_offsets.ctypes.data_as(c_void_p), - field_sizes.ctypes.data_as(c_void_p), - field_types.ctypes.data_as(c_void_p)) - assert field_sizes.all(), 'At least one field seems to be missing' - - # The following rather convoluted expression converts the separate - # arrays of field names and attributes into a dictionary looking up - # each field and returning the appropriate list of attributes. - # One final adjustment needed is that all the numpy.int16 values - # need to be converted back into regular integers to ensure good - # processing downstream. - self.fields = dict(zip(fields, zip( - *map(lambda l: map(int, l), - (field_offsets, field_sizes, field_types))))) + self.fields = get_field_offsets(record_type) + missing = set(fields) - set(self.fields) + assert not missing, \ + "Fields not supported by %s: %s" % (record_type, sorted(missing)) def __call__(self, record): '''Converts a raw pointer to a record structure into a _Record object diff --git a/softioc/imports.py b/softioc/imports.py index c15a5e9a..74af7716 100644 --- a/softioc/imports.py +++ b/softioc/imports.py @@ -6,6 +6,26 @@ import sys from ctypes import * +from epicscorelibs import path + +from . import _extension + +# These are in the extension +def get_DBF_values(): + """Return {DBF_name: DBF_int_value} mapping""" + return _extension.get_DBF_values() + +def get_field_offsets(record_type): + """Return {field_name: (offset, size, field_type)}""" + return _extension.get_field_offsets(record_type) + +def db_put_field(name, dbr_type, pbuffer, length): + """Put field where pbuffer is void* pointer. Returns RC""" + return _extension.db_put_field(name, dbr_type, pbuffer, length) + +def install_pv_logging(acf_file): + """Install pv logging""" + _extension.install_pv_logging(acf_file) def expect_success(status, function, args): assert status == 0, 'Expected success' @@ -37,56 +57,9 @@ def auto_decode(result, func, args): return result.decode() -libPythonSupport = CDLL('libPythonSupport.so') - -# void get_field_offsets( -# const char * record_type, const char * field_names[], int field_count, -# short field_offset[], short field_size[], short field_type[]) -# -# Looks up field offset, size and type values for the given record type and -# the given list of field names. -get_field_offsets = libPythonSupport.get_field_offsets -get_field_offsets.argtypes = ( - auto_encode, c_void_p, c_int, c_void_p, c_void_p, c_void_p) -get_field_offsets.restype = None - -# int db_put_field(const char *name, short dbrType, void *pbuffer, long length) -# -# Updates value in given field through channel access, so notifications are -# generated as appropriate. -db_put_field = libPythonSupport.db_put_field -db_put_field.argtypes = (auto_encode, c_int, c_void_p, c_long) -db_put_field.errcheck = expect_success - - -# const char *get_EPICS_BASE(void) -# -# Returns the path to EPICS_BASE -get_EPICS_BASE = libPythonSupport.get_EPICS_BASE -get_EPICS_BASE.argtypes = () -get_EPICS_BASE.restype = c_char_p -get_EPICS_BASE.errcheck = auto_decode - - -# PyObject *get_DBF_values(void) -# -# Returns dictionary mapping DBF_ enum names to values -get_DBF_values = libPythonSupport.get_DBF_values -get_DBF_values.restype = py_object - - -# void EpicsPvPutHook(struct asTrapWriteMessage *pmessage, int after) -# -# Hook for logging EPICS caput events -EpicsPvPutHook = libPythonSupport.EpicsPvPutHook - - -EPICS_BASE = get_EPICS_BASE() -EPICS_HOST_ARCH = os.environ['EPICS_HOST_ARCH'] - def EpicsDll(dll): - return CDLL( - os.path.join(EPICS_BASE, 'lib', EPICS_HOST_ARCH, 'lib%s.so' % dll)) + return CDLL(path.get_lib(dll)) + # A bit tricky: in more recent versions of EPICS all the entry points we want # have been gathered into a single .so, but previously they were split among @@ -96,14 +69,11 @@ def EpicsDll(dll): libregistryIoc = libdbCore libdbIoc = libdbCore libmiscIoc = libdbCore - libasIoc = libdbCore except OSError: # Ok, no dbCore, then we should find everything in these four instead. libregistryIoc = EpicsDll('registryIoc') libdbIoc = EpicsDll('dbIoc') libmiscIoc = EpicsDll('miscIoc') - libasIoc = EpicsDll('asIoc') - # int registryDeviceSupportAdd( @@ -150,20 +120,6 @@ def EpicsDll(dll): epicsExit.argtypes = () -# Import for libas - -# int asSetFilename(const char *acf) -# -# Set access control file -asSetFilename = libasIoc.asSetFilename -asSetFilename.argtypes = (auto_encode,) - -# asTrapWriteId asTrapWriteRegisterListener(asTrapWriteListener func) -# -# Install caput hook -asTrapWriteRegisterListener = libasIoc.asTrapWriteRegisterListener - - __all__ = [ 'get_field_offsets', 'registryDeviceSupportAdd', diff --git a/softioc/iocStats b/softioc/iocStats new file mode 160000 index 00000000..4df9e878 --- /dev/null +++ b/softioc/iocStats @@ -0,0 +1 @@ +Subproject commit 4df9e87815f6a9432955a3ddb45fafa9fe4a4d40 diff --git a/softioc/pvlog.py b/softioc/pvlog.py index b245ead1..6a4fb9aa 100644 --- a/softioc/pvlog.py +++ b/softioc/pvlog.py @@ -5,6 +5,5 @@ import os from . import imports -imports.asSetFilename( - os.path.join(os.path.dirname(__file__), '..', 'access.acf')) -imports.asTrapWriteRegisterListener(imports.EpicsPvPutHook) +imports.install_pv_logging( + os.path.join(os.path.dirname(__file__), 'access.acf')) diff --git a/softioc/softioc.py b/softioc/softioc.py index 1a1798fd..7b19d1f6 100644 --- a/softioc/softioc.py +++ b/softioc/softioc.py @@ -157,9 +157,9 @@ def dbLoadDatabase(database, path = None, substitutions = None): def devIocStats(ioc_name): dbLoadDatabase( - 'ioc.db', os.path.join(os.getenv('HERE'), 'db'), - 'IOCNAME=%s,name=' % ioc_name) - + "ioc.template", + os.path.join(os.path.dirname(__file__), "iocStats", "iocAdmin", "Db"), + 'IOCNAME=' + ioc_name + ',TODFORMAT=%m/%d/%Y %H:%M:%S') def interactive_ioc(context = {}, call_exit = True): '''Fires up the interactive IOC prompt with the given context.''' diff --git a/tests/test_ioc.py b/tests/test_ioc.py index 68b5d089..418d5ba1 100644 --- a/tests/test_ioc.py +++ b/tests/test_ioc.py @@ -1,11 +1,9 @@ # Simple example script for building an example soft IOC. -import versions - import cothread from softioc import softioc, builder, pvlog -from testing import * +from sim_records import * softioc.devIocStats('TS-DI-TEST-01')