Skip to content
Open
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
6 changes: 0 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ jobs:
include:
# see compat matrix: https://github.com/pypa/manylinux

- ml: manylinux1_x86_64
py: cp27-cp27m
cy: "Cython<3.0"
numpy: numpy==1.8.2
skip: true

- ml: manylinux2014_x86_64
py: cp38-cp38
cy: "Cython<3.1"
Expand Down
24 changes: 6 additions & 18 deletions documentation/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,21 @@ Release tars available from https://github.com/epics-base/p4p/releases

The P4P modules requires:

* Python 2.7 or >=3.5 (>=3.6 for asyncio support)
* numpy >=1.6
* Cython >=0.25.2
* Python >=3.8.
* numpy >=1.7
* Cython >=3.0.0a1
* nose2 (Optional, recommended to run unittests)

and...

Current (>= 4.0)

* EPICS Base >= 3.14.12
* PVXS >= 0.2.0

Prior to 4.0

* EPICS >= 7.0.2

or

* EPICS Base >= 3.14.12
* pvDataCPP >=7.1.0
* pvAccessCPP >=6.1.0

P4P can be built and installed in one of two ways.
As a python package, preferably managed by PIP.
As an EPICS module.

Optional
Optional Dependencies:

* `cothread <https://github.com/dls-controls/cothread>`_ needed by `p4p.client.cothread.Context`.
* `qtpy <https://github.com/spyder-ide/qtpy>`_ needed for `p4p.client.Qt.Context`.
Expand Down Expand Up @@ -80,7 +68,7 @@ Then prepare the virtualenv env with. ::
tar -xaf p4p-env-*.tar.gz
python virtualenv.py --never-download env
. env/bin/activate
pip install --no-index -f virtualenv_support p4p
pip install --no-index -f ./virtualenv_support p4p
python -m nose2 p4p # Optional: runs automatic tests

Utilities to automate this process include https://pypi.org/project/pyutilib.virtualenv/
Expand Down Expand Up @@ -173,4 +161,4 @@ To run the unittests: ::
For testing purposes several simple command line client tools are provided.
For further information run: ::

PYTHONPATH=$PWD/python2.7/linux-x86_64 python -m p4p.client.cli -h
PYTHONPATH=$PWD/python3.8/linux-x86_64 python -m p4p.client.cli -h
1 change: 0 additions & 1 deletion documentation/gw.rst
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ These values are aggregated from all defined internal gateway Servers and Client
May be useful for detecting resource leaks while troubleshooting.

**<statusprefix>threads**
Available when running with python >= 3.5.
An RPC call which returns a text description of all python threads.

.. note::
Expand Down
2 changes: 1 addition & 1 deletion documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ https://epics.anl.gov/
P4P includes API for a `clientapi` and `serverapi`,
as well as a `gwpage` executable (``pvagw``) which can optionally enforce an access control policy.

Supports Linux, OSX, and Windows. With python 2.7 and >=3.5 (>=3.6 for asyncio support).
Supports Linux, OSX, and Windows. With python >=3.8.

The recommended starting point is to install from pypi.org. ::

Expand Down
4 changes: 2 additions & 2 deletions documentation/values.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Type specifier codes:
Code Type
==== =======
? bool
s unicode
s str
b s8
B u8
h s16
Expand Down Expand Up @@ -175,7 +175,7 @@ Value Stores a structure
int signed 32-bit (python 2.x only)
long signed 64-bit
float 64-bit floating
bytes|unicode string
bytes|str string
ndarray array of integer or floating
============= ================================

Expand Down
25 changes: 3 additions & 22 deletions makehelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Emits something like the following

PY_OK := YES # indicates success of this script
PY_VER := 2.6
PY_VER := 3.8
PY_INCDIRS := /path ...
PY_LIBDIRS := /path ...
"""
Expand All @@ -24,12 +24,7 @@
pass
out = open(sys.argv[1], 'w')

try:
from sysconfig import get_config_var, get_path
def get_python_inc():
return get_path('include')
except ImportError:
from distutils.sysconfig import get_config_var, get_python_inc
from sysconfig import get_config_var, get_path

def gcv(name, *dflt):
v = get_config_var(name)
Expand All @@ -39,7 +34,7 @@ def gcv(name, *dflt):
raise KeyError(name)
return v

incdirs = [get_python_inc()]
incdirs = [get_path('include')]
libdir = gcv('LIBDIR', '') or gcv('prefix') + '/libs'


Expand All @@ -65,20 +60,6 @@ def get_numpy_include_dirs():
print('PY_INCDIRS :=',' '.join(incdirs), file=out)
print('PY_LIBDIRS :=',libdir, file=out)

try:
import asyncio
except ImportError:
print('HAVE_ASYNCIO := NO', file=out)
else:
print('HAVE_ASYNCIO := YES', file=out)

try:
import cothread
except ImportError:
print('HAVE_COTHREAD := NO', file=out)
else:
print('HAVE_COTHREAD := YES', file=out)

print('PY_OK := YES', file=out)

out.close()
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ requires = [
"setuptools_dso>=2.7a1",
"setuptools_dso>=2.11a2; python_version>='3.12'",
"wheel",
"numpy>=1.7",
"Cython>0.29.32, <3.0.0a1 ; python_version<'3.0'",
"Cython>0.29.32, <3.1.0a1 ; python_version>='3.0' and python_version<'3.9'",
"numpy>=1.15",
"Cython>0.29.32, <3.1.0a1 ; python_version<'3.9'",
"Cython>0.29.32 ; python_version>='3.9'",
"epicscorelibs>=7.0.3.99.2.0a1",
"epicscorelibs>=7.0.7.99.1.2a2; python_version>='3.12'",
Expand Down
6 changes: 2 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def get_numpy_include_dirs():
]

if hasattr(numpy.lib, "NumpyVersion") and numpy.lib.NumpyVersion(numpy.__version__) >= '2.0.0b1':
install_requires += ['numpy >= 1.7', 'numpy < 3']
install_requires += ['numpy >= 1.15', 'numpy < 3']
else:
# assume ABI forward compatibility as indicated by
# https://github.com/numpy/numpy/blob/master/numpy/core/setup_common.py#L28
Expand All @@ -121,10 +121,8 @@ def get_numpy_include_dirs():
license='BSD',
classifiers = [
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: Implementation :: CPython',
'License :: OSI Approved :: BSD License',
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering',
'Topic :: Software Development :: Libraries',
Expand All @@ -134,7 +132,7 @@ def get_numpy_include_dirs():
'Operating System :: Microsoft :: Windows',
],
keywords='epics scada',
python_requires='>=2.7',
python_requires='>=3.8',

packages=[
'p4p',
Expand Down
1 change: 0 additions & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ PY += p4p/test/test_qt.py
PY += p4p/test/qttest.py

PY += p4p/test/test_asyncio.py
PY += p4p/test/asynciotest.py

PY += p4p/example.conf
PY += p4p/pvagw@.service
Expand Down
19 changes: 3 additions & 16 deletions src/p4p.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,18 @@

#include <Python.h>

#if PY_MAJOR_VERSION < 3
// quiet some warnings about implicit const char* -> char* cast
// for API functions. These are corrected in py >= 3.x
#ifndef PyObject_CallFunction
# define PyObject_CallFunction(O, FMT, ...) PyObject_CallFunction(O, (char*)(FMT), ##__VA_ARGS__)
# define PyObject_CallMethod(O, METH, FMT, ...) PyObject_CallMethod(O, (char*)(METH), (char*)(FMT), ##__VA_ARGS__)
#endif
#endif

// handle -fvisibility=default
// effects generated _p4p.cpp
#if __GNUC__ >= 4
# undef PyMODINIT_FUNC
# if PY_MAJOR_VERSION < 3
# define PyMODINIT_FUNC extern "C" __attribute__ ((visibility("default"))) void
# else
# define PyMODINIT_FUNC extern "C" __attribute__ ((visibility("default"))) PyObject*
# endif
# define PyMODINIT_FUNC extern "C" __attribute__ ((visibility("default"))) PyObject*
#endif

// this file must be included from all code using numpy APIs
// include cython generated code.
// minimum supported numpy version...
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#define NPY_TARGET_VERSION NPY_1_7_API_VERSION
#define NPY_NO_DEPRECATED_API NPY_1_15_API_VERSION
#define NPY_TARGET_VERSION NPY_1_15_API_VERSION

namespace p4p {

Expand Down
2 changes: 1 addition & 1 deletion src/p4p/_gw.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ cdef class Provider(_p4p.Source):
self.BanPV = GWSearchBanPV
self.BanHostPV = GWSearchBanHostPV

def __init__(self, unicode name, object client, object handler):
def __init__(self, str name, object client, object handler):
cdef _p4p.ClientProvider prov = client._ctxt
cdef string cname = name.encode('utf-8')
self.name = cname
Expand Down
2 changes: 1 addition & 1 deletion src/p4p/_p4p.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ cdef lookupMember(data.Value* dest, const data.Value& top, key, int err):
if key is None:
dest[0] = top
return
elif isinstance(key, unicode):
elif isinstance(key, str):
ckey = key.encode()
else:
ckey = key
Expand Down
18 changes: 1 addition & 17 deletions src/p4p/client/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,7 @@
'timesout',
]

if hasattr(asyncio, 'get_running_loop'): # py >=3.7
from asyncio import get_running_loop, create_task, all_tasks
else:
from asyncio import _get_running_loop
from asyncio.tasks import Task

def get_running_loop():
ret = _get_running_loop()
if ret is None:
raise RuntimeError('Thread has no running event loop')
return ret

def create_task(coro, *, name=None):
return get_running_loop().create_task(coro)

def all_tasks():
return Task.all_tasks(loop=get_running_loop())
from asyncio import get_running_loop, create_task, all_tasks

def timesout(deftimeout=5.0):
"""Decorate a coroutine to implement an overall timeout.
Expand Down
9 changes: 2 additions & 7 deletions src/p4p/client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,19 @@
import sys
import time
import json
try:
from itertools import izip
except ImportError:
izip = zip

import logging
_log = logging.getLogger(__name__)

from .. import nt
from .. import set_debug
from . import thread

_log = logging.getLogger(__name__)

def op_get(ctxt, args):
requests = [args.request] * len(args.names)
results = ctxt.get(args.names, requests, timeout=args.timeout, throw=False)
ret = 0
for name, val in izip(args.names, results):
for name, val in zip(args.names, results):
if isinstance(val, Exception):
ret = 1
print(name, 'Error:', val)
Expand Down
7 changes: 2 additions & 5 deletions src/p4p/client/cothread.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
'RemoteError',
]

if sys.version_info >= (3, 0):
unicode = str

def cb2event(done, value):
if isinstance(value, Cancelled):
pass # can ignore cothreads can't be preempted
Expand Down Expand Up @@ -56,7 +53,7 @@ def get(self, name, request=None, timeout=5.0, throw=True):
>>> A, B = ctxt.get(['pv:1', 'pv:2'])
>>>
"""
singlepv = isinstance(name, (bytes, unicode))
singlepv = isinstance(name, (bytes, str))
if singlepv:
return self._get_one(name, request=request, timeout=timeout, throw=throw)

Expand Down Expand Up @@ -124,7 +121,7 @@ def put(self, name, values, request=None, process=None, wait=None, timeout=5.0,
Unless the provided value is a dict, it is assumed to be a plain value
and an attempt is made to store it in '.value' field.
"""
singlepv = isinstance(name, (bytes, unicode))
singlepv = isinstance(name, (bytes, str))
if request and (process or wait is not None):
raise ValueError("request= is mutually exclusive to process= or wait=")
elif process or wait is not None:
Expand Down
28 changes: 8 additions & 20 deletions src/p4p/client/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,14 @@
import warnings
import sys

try:
from Queue import Queue, Full, Empty
except ImportError:
from queue import Queue, Full, Empty
from queue import Queue, Full, Empty

from .. import _p4p
from .._p4p import Cancelled, Disconnected, Finished, RemoteError

from ..wrapper import Value, Type
from ..nt import buildNT

if sys.version_info >= (3, 0):
unicode = str

__all__ = (
'Subscription',
'Context',
Expand All @@ -40,10 +34,7 @@ def dounwrap(code, msg, val, handler=handler):
handler(Cancelled())
elif code == 2: # exception during builder callback
A, B, C = val
if unicode is str:
E = A(B).with_traceback(C) # py 3
else:
E = A(B) # py 2 (bye bye traceback...)
E = A(B).with_traceback(C)
handler(E)
else:
if val is not None:
Expand Down Expand Up @@ -127,9 +118,8 @@ def __enter__(self):
def __exit__(self, A, B, C):
self.close()

if unicode is str:
def __del__(self):
self.close()
def __del__(self):
self.close()

class Context(object):

Expand Down Expand Up @@ -268,11 +258,9 @@ def _cleanup_contexts():
ctxt.close()

class _ClientOperation(_p4p.ClientOperation):
if unicode is str:
def __del__(self):
self.close()
def __del__(self):
self.close()

class _ClientProvider(_p4p.ClientProvider):
if unicode is str:
def __del__(self):
self.close()
def __del__(self):
self.close()
Loading
Loading