Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ResourceWarning by avoiding garbage collection altogether #5

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
8 changes: 3 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# Config file for automatic testing at travis-ci.org

language: python
dist: xenial
dist: bionic
python:
- "3.9-dev"
- "3.10-dev"
- "3.9"
- "3.8"
- "3.7"
- "3.6"
- "3.5"
- "2.7"
- "pypy"
- "pypy3"

# Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
- Avoid `ResourceWarning: unclosed file <...>` warning by keeping a strong reference to the lock files. Lock files were weakly referenced on purpose, so that they could stay alive until the interpreter decides to remove them.
- Drop support for python 2.7 and 3.5. Only python >= 3.6 is supported now.

## 0.2.0
- Removed logic that falls back to copying a file when `file_movable=True` but the file does not appear to be movable. Do not try to be smart.
Expand Down
17 changes: 15 additions & 2 deletions dogpile_filesystem/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@

logger = logging.getLogger(__name__)

file_lock = ProcessLocalRegistry(lambda path: open(path, "w+b"))
_registry = {}


def _open_file(path):
"""Open a file and keep a reference to it, in order to avoid ResourceWarnings when the interpreter
closes the file automatically when garbage collected."""
try:
return _registry[path]
except KeyError:
f = _registry[path] = open(path, "w+b", buffering=0)
return f


file_lock = ProcessLocalRegistry(creator=_open_file)


def _lock_creator(identifier):
Expand All @@ -14,4 +27,4 @@ def _lock_creator(identifier):
return RangedFileReentrantLock(file_o, offset)


locks = ProcessLocalRegistry(_lock_creator)
locks = ProcessLocalRegistry(creator=_lock_creator)
7 changes: 4 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@
"License :: OSI Approved :: MIT License",
"Natural Language :: English",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
description="File System Backends for Dogpile Cache",
install_requires=["dogpile.cache"],
python_requires=">=3.6",
license="MIT license",
long_description=readme + "\n\n" + history,
long_description_content_type="text/markdown",
Expand Down
33 changes: 27 additions & 6 deletions tests/test_locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,24 @@ def other_thread():
mutex.release()


def test_dogpile_lock_processes(region):
def _other_process(
backend_name, region_expiration_time, backend_arguments, proc_result
):
import dogpile.cache

region = dogpile.cache.make_region("test_region")
region.configure(
backend=backend_name,
expiration_time=region_expiration_time,
arguments=backend_arguments,
)
o_mutex = region.backend.get_mutex("asd")
proc_result.value = o_mutex.acquire(False)


def test_dogpile_lock_processes(
region, backend_name, region_expiration_time, backend_arguments
):
mutex = region.backend.get_mutex("asd")

mutex.acquire()
Expand All @@ -47,11 +64,15 @@ def test_dogpile_lock_processes(region):
proc_result = multiprocessing.Value("d", 42)
assert proc_result.value == 42

def other_process():
o_mutex = region.backend.get_mutex("asd")
proc_result.value = o_mutex.acquire(False)

t = multiprocessing.Process(target=other_process)
t = multiprocessing.Process(
target=_other_process,
kwargs={
"backend_name": backend_name,
"region_expiration_time": region_expiration_time,
"backend_arguments": backend_arguments,
"proc_result": proc_result,
},
)
t.start()
t.join()

Expand Down
21 changes: 8 additions & 13 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
[tox]
envlist =
py38-linters,
py27-dp{06,07,08,09},
py35-dp{06,07,08,09},
py36-dp{06,07,08,09,10,latest},
py37-dp{06,07,08,09,10,latest},
py38-dp{06,07,08,09,10,latest},
py39-dp{06,07,08,09,10,latest},
pypy-dp{06,07,08,09},
pypy3-dp{06,07,08,09,10,latest}
py36-dp{10,11,latest},
py37-dp{10,11,latest},
py38-dp{10,11,latest},
py39-dp{10,11,latest},
py310-dp{10,11,latest},
pypy3-dp{10,11,latest}

[testenv:py37-linters]
[testenv:py38-linters]
deps = black
commands = black --check --verbose setup.py dogpile_filesystem tests

[testenv]
deps =
dp10: dogpile.cache~=1.0.0
dp09: dogpile.cache~=0.9.0
dp08: dogpile.cache~=0.8.0
dp07: dogpile.cache~=0.7.0
dp06: dogpile.cache~=0.6.0
dp11: dogpile.cache~=1.1.0
dplatest: dogpile.cache
-r{toxinidir}/requirements_dev.txt
commands =
Expand Down