refcount


Namerefcount JSON
Version 1.2.0 PyPI version JSON
download
home_pagehttps://github.com/csiro-hydroinformatics/pyrefcount
SummaryA Python package for reference counting and interop with native pointers
upload_time2023-01-25 00:54:24
maintainer
docs_urlNone
authorJean-Michel Perraud
requires_python
license
keywords interop python native-libraries reference-counting handle cffi
VCS
bugtrack_url
requirements cffi typing-extensions
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # refcount - Python classes for reference counting

[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/csiro-hydroinformatics/pyrefcount/blob/master/LICENSE.txt) ![status](https://img.shields.io/badge/status-stable-green.svg) [![Documentation Status](https://readthedocs.org/projects/pyrefcount/badge/?version=latest)](https://pyrefcount.readthedocs.io/en/latest/?badge=latest) [![codecov](https://codecov.io/gh/csiro-hydroinformatics/pyrefcount/branch/master/graph/badge.svg?token=ZBBFq3ncAq)](https://codecov.io/gh/csiro-hydroinformatics/pyrefcount) master: [![Python package](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml/badge.svg?branch=master)](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml) testing: [![Python package](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml/badge.svg?branch=testing)](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml)


![Reference counted native handles](./docs/img/refcount-principles.png "Reference counted native handles")

This package is primarily for managing resources in native libraries, written for instance in C++, from Python. While it boils down to "simply" maintaining a set of counters, **it is deceptively complicated to do so properly** and not end up with memory leaks or crashes. This package offers structured options for reliably managing external native resources. Surprisingly I could not locate an existing package doing just what I needed. Other use cases requiring reference counting, aside from native library resources, may benefit from reusing and extending classes in `refcount`.

`refcount` ( >=0.7) includes classes using [cffi](https://cffi.readthedocs.io/). Other low-level interoperability mechanisms may well be added in the future.

## License

MIT (see [License.txt](https://github.com/csiro-hydroinformatics/pyrefcount/blob/master/LICENSE.txt))

## Documentation

Hosted at [refcount via readthedocs.io](https://pyrefcount.readthedocs.io/en/latest/?badge=latest)

## Source code

The code repository is on [GitHub](https://github.com/csiro-hydroinformatics/pyrefcount).

## Installation

### conda-forge

Using `conda` or `mamba`:

```sh
mamba install -c conda-forge refcount
```

### pypi

```sh
pip install refcount
```

### From source (development)

```sh
pip install -r requirements.txt
pip install -e .
```

## Sample use

The following example is based on one of the unit tests.

Say we have a C++ library with objects and a C API:

```C++
#define TEST_DOG_PTR  testnative::dog*
#define TEST_OWNER_PTR  testnative::owner*
#define TEST_COUNTED_PTR  testnative::reference_counter*

testnative::dog* create_dog();
testnative::owner* create_owner(testnative::dog* d);
void say_walk(testnative::owner* owner);
void release(testnative::reference_counter* obj);
// etc.
```

From the outside of the library the API is exported with opaque pointers `void*` (C structs pointers and native C99 types could be handled too).

```C++
void* create_dog();
void* create_owner(void* d);
void say_walk(void* owner);
void release(void* obj);
// etc.
```

Starting with the end in mind, from Python we want an API hiding the low level details close to the C API, in particular avoiding managing native memory via `release` C API calls, piggybacking the python GC instead.

```python
dog = Dog()
owner = DogOwner(dog)
owner.say_walk()
print(dog.position)
dog = None # the "native dog" is still alive though, as the owner incremented the ref count
owner = None
```

This is doable with `refcount` and the `cffi` package. One possible design is:

```python
ut_ffi = cffi.FFI()

ut_ffi.cdef('extern void* create_dog();')
ut_ffi.cdef('extern void* create_owner( void* d);')
ut_ffi.cdef('extern void say_walk( void* owner);')
ut_ffi.cdef('extern void release( void* obj);')
# etc.

ut_dll = ut_ffi.dlopen('c:/path/to/test_native_library.dll', 1) # Lazy loading

class CustomCffiNativeHandle(CffiNativeHandle):
    def __init__(self, pointer, prior_ref_count = 0):
        super(CustomCffiNativeHandle, self).__init__(pointer, type_id='', prior_ref_count = prior_ref_count)

    def _release_handle(self) -> bool:
        ut_dll.release(self.get_handle())
        return True

class Dog(CustomCffiNativeHandle):
    def __init__(self, pointer = None):
        if pointer is None:
            pointer = ut_dll.create_dog()
        super(Dog, self).__init__(pointer)
    # etc.

class DogOwner(CustomCffiNativeHandle):

    def __init__(self, dog):
        super(DogOwner, self).__init__(None)
        self._set_handle(ut_dll.create_owner(dog.get_handle()))
        self.dog = dog
        self.dog.add_ref() # Do note this important reference increment

    def say_walk(self):
        ut_dll.say_walk(self.get_handle())

    def _release_handle(self) -> bool:
        super(DogOwner, self)._release_handle()
        # super(DogOwner, self)._release_handle()
        self.dog.release()
        return True
```

## Related work

### Ancestry, acknowledgements

This python package `refcount` actually spawned from prior work for interoperability between C++, R and .NET ([R.NET](https://github.com/rdotnet/rdotnet))

`refcount` features using `cffi` were also significantly informed by Kevin Plastow's [work](https://search.informit.com.au/documentSummary;dn=823898220073899;res=IELENG) while he was at the Australian Bureau of Meteorology; this contribution is gratefully acknowledged.

In you have native interop needs you may also want to look at:

* the nuget package [dynamic-interop-dll](https://github.com/rdotnet/dynamic-interop-dll) for .NET/native interop.
* a set of mostly c++ software [tools for interop with C/C++](https://github.com/csiro-hydroinformatics/c-interop)
* a C# library for [generating interop glue code on top of C API glue code](https://github.com/csiro-hydroinformatics/c-api-wrapper-generation).

### Other python packages

`refcount` was created in part because no existing prior (Python) work could quite fit the need. There are however packages that may better address your particular need:

* [infi.pyutils](https://pypi.org/project/infi.pyutils/) contains a reference counting class.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/csiro-hydroinformatics/pyrefcount",
    "name": "refcount",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "interop python native-libraries reference-counting handle cffi",
    "author": "Jean-Michel Perraud",
    "author_email": "per202@csiro.au",
    "download_url": "https://files.pythonhosted.org/packages/5f/ad/f8210b5db6f98d8607b7c7c726bd0198de46c323fc27b3066ffae0c8653b/refcount-1.2.0.zip",
    "platform": null,
    "description": "# refcount - Python classes for reference counting\n\n[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/csiro-hydroinformatics/pyrefcount/blob/master/LICENSE.txt) ![status](https://img.shields.io/badge/status-stable-green.svg) [![Documentation Status](https://readthedocs.org/projects/pyrefcount/badge/?version=latest)](https://pyrefcount.readthedocs.io/en/latest/?badge=latest) [![codecov](https://codecov.io/gh/csiro-hydroinformatics/pyrefcount/branch/master/graph/badge.svg?token=ZBBFq3ncAq)](https://codecov.io/gh/csiro-hydroinformatics/pyrefcount) master: [![Python package](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml/badge.svg?branch=master)](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml) testing: [![Python package](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml/badge.svg?branch=testing)](https://github.com/csiro-hydroinformatics/pyrefcount/actions/workflows/build-matrix.yml)\n\n\n![Reference counted native handles](./docs/img/refcount-principles.png \"Reference counted native handles\")\n\nThis package is primarily for managing resources in native libraries, written for instance in C++, from Python. While it boils down to \"simply\" maintaining a set of counters, **it is deceptively complicated to do so properly** and not end up with memory leaks or crashes. This package offers structured options for reliably managing external native resources. Surprisingly I could not locate an existing package doing just what I needed. Other use cases requiring reference counting, aside from native library resources, may benefit from reusing and extending classes in `refcount`.\n\n`refcount` ( >=0.7) includes classes using [cffi](https://cffi.readthedocs.io/). Other low-level interoperability mechanisms may well be added in the future.\n\n## License\n\nMIT (see [License.txt](https://github.com/csiro-hydroinformatics/pyrefcount/blob/master/LICENSE.txt))\n\n## Documentation\n\nHosted at [refcount via readthedocs.io](https://pyrefcount.readthedocs.io/en/latest/?badge=latest)\n\n## Source code\n\nThe code repository is on [GitHub](https://github.com/csiro-hydroinformatics/pyrefcount).\n\n## Installation\n\n### conda-forge\n\nUsing `conda` or `mamba`:\n\n```sh\nmamba install -c conda-forge refcount\n```\n\n### pypi\n\n```sh\npip install refcount\n```\n\n### From source (development)\n\n```sh\npip install -r requirements.txt\npip install -e .\n```\n\n## Sample use\n\nThe following example is based on one of the unit tests.\n\nSay we have a C++ library with objects and a C API:\n\n```C++\n#define TEST_DOG_PTR  testnative::dog*\n#define TEST_OWNER_PTR  testnative::owner*\n#define TEST_COUNTED_PTR  testnative::reference_counter*\n\ntestnative::dog* create_dog();\ntestnative::owner* create_owner(testnative::dog* d);\nvoid say_walk(testnative::owner* owner);\nvoid release(testnative::reference_counter* obj);\n// etc.\n```\n\nFrom the outside of the library the API is exported with opaque pointers `void*` (C structs pointers and native C99 types could be handled too).\n\n```C++\nvoid* create_dog();\nvoid* create_owner(void* d);\nvoid say_walk(void* owner);\nvoid release(void* obj);\n// etc.\n```\n\nStarting with the end in mind, from Python we want an API hiding the low level details close to the C API, in particular avoiding managing native memory via `release` C API calls, piggybacking the python GC instead.\n\n```python\ndog = Dog()\nowner = DogOwner(dog)\nowner.say_walk()\nprint(dog.position)\ndog = None # the \"native dog\" is still alive though, as the owner incremented the ref count\nowner = None\n```\n\nThis is doable with `refcount` and the `cffi` package. One possible design is:\n\n```python\nut_ffi = cffi.FFI()\n\nut_ffi.cdef('extern void* create_dog();')\nut_ffi.cdef('extern void* create_owner( void* d);')\nut_ffi.cdef('extern void say_walk( void* owner);')\nut_ffi.cdef('extern void release( void* obj);')\n# etc.\n\nut_dll = ut_ffi.dlopen('c:/path/to/test_native_library.dll', 1) # Lazy loading\n\nclass CustomCffiNativeHandle(CffiNativeHandle):\n    def __init__(self, pointer, prior_ref_count = 0):\n        super(CustomCffiNativeHandle, self).__init__(pointer, type_id='', prior_ref_count = prior_ref_count)\n\n    def _release_handle(self) -> bool:\n        ut_dll.release(self.get_handle())\n        return True\n\nclass Dog(CustomCffiNativeHandle):\n    def __init__(self, pointer = None):\n        if pointer is None:\n            pointer = ut_dll.create_dog()\n        super(Dog, self).__init__(pointer)\n    # etc.\n\nclass DogOwner(CustomCffiNativeHandle):\n\n    def __init__(self, dog):\n        super(DogOwner, self).__init__(None)\n        self._set_handle(ut_dll.create_owner(dog.get_handle()))\n        self.dog = dog\n        self.dog.add_ref() # Do note this important reference increment\n\n    def say_walk(self):\n        ut_dll.say_walk(self.get_handle())\n\n    def _release_handle(self) -> bool:\n        super(DogOwner, self)._release_handle()\n        # super(DogOwner, self)._release_handle()\n        self.dog.release()\n        return True\n```\n\n## Related work\n\n### Ancestry, acknowledgements\n\nThis python package `refcount` actually spawned from prior work for interoperability between C++, R and .NET ([R.NET](https://github.com/rdotnet/rdotnet))\n\n`refcount` features using `cffi` were also significantly informed by Kevin Plastow's [work](https://search.informit.com.au/documentSummary;dn=823898220073899;res=IELENG) while he was at the Australian Bureau of Meteorology; this contribution is gratefully acknowledged.\n\nIn you have native interop needs you may also want to look at:\n\n* the nuget package [dynamic-interop-dll](https://github.com/rdotnet/dynamic-interop-dll) for .NET/native interop.\n* a set of mostly c++ software [tools for interop with C/C++](https://github.com/csiro-hydroinformatics/c-interop)\n* a C# library for [generating interop glue code on top of C API glue code](https://github.com/csiro-hydroinformatics/c-api-wrapper-generation).\n\n### Other python packages\n\n`refcount` was created in part because no existing prior (Python) work could quite fit the need. There are however packages that may better address your particular need:\n\n* [infi.pyutils](https://pypi.org/project/infi.pyutils/) contains a reference counting class.\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "A Python package for reference counting and interop with native pointers",
    "version": "1.2.0",
    "split_keywords": [
        "interop",
        "python",
        "native-libraries",
        "reference-counting",
        "handle",
        "cffi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "391d31a132cb4efc39111bed3a0f44c9850ca18fc22030aad3f9e5788a72d425",
                "md5": "899cac5dc5ddf5bb876a56b5da8d3d4f",
                "sha256": "b9934a38d7d9a5d644c21c3cbc10de69be7db0cb97a49294dff7306024d0782b"
            },
            "downloads": -1,
            "filename": "refcount-1.2.0-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "899cac5dc5ddf5bb876a56b5da8d3d4f",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 15767,
            "upload_time": "2023-01-25T00:54:23",
            "upload_time_iso_8601": "2023-01-25T00:54:23.379956Z",
            "url": "https://files.pythonhosted.org/packages/39/1d/31a132cb4efc39111bed3a0f44c9850ca18fc22030aad3f9e5788a72d425/refcount-1.2.0-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5fadf8210b5db6f98d8607b7c7c726bd0198de46c323fc27b3066ffae0c8653b",
                "md5": "bb153dcedb911c39cdf792ac5c8969e5",
                "sha256": "9672137bdc67b20f6442ac8304494b1c2bd00de5907f5009b202a38a17448738"
            },
            "downloads": -1,
            "filename": "refcount-1.2.0.zip",
            "has_sig": false,
            "md5_digest": "bb153dcedb911c39cdf792ac5c8969e5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 24865,
            "upload_time": "2023-01-25T00:54:24",
            "upload_time_iso_8601": "2023-01-25T00:54:24.853336Z",
            "url": "https://files.pythonhosted.org/packages/5f/ad/f8210b5db6f98d8607b7c7c726bd0198de46c323fc27b3066ffae0c8653b/refcount-1.2.0.zip",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-25 00:54:24",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "csiro-hydroinformatics",
    "github_project": "pyrefcount",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "appveyor": true,
    "requirements": [
        {
            "name": "cffi",
            "specs": [
                [
                    ">=",
                    "1.11.5"
                ]
            ]
        },
        {
            "name": "typing-extensions",
            "specs": [
                [
                    ">=",
                    "4.3.0"
                ]
            ]
        }
    ],
    "lcname": "refcount"
}
        
Elapsed time: 0.03062s