Allow a single instance of a Python application to run at once
Project description
Socket_Singleton.py
Socket-based, single-instance Python applications with a clean interface
Without lockfiles, mutexes, dependencies, or tomfoolery
Install:
pip install Socket_Singleton
Import:
From Socket_Singleton import Socket_Singleton
Constructor:
Socket_Singleton(address="127.0.0.1", port=1337, timeout=0, client=True, strict=True, max_clients=0)
Usage:
Socket_Singleton()
or, keep a reference:
app = Socket_Singleton()
then attach a callback, and capture arguments from subsequent calls to your application:
def my_callback(arg):
print(arg)
app.trace(my_callback)
See also:
It is recommended to specify a port in the constructor*
Examples:
Say we have an application, app.py, that we want to restrict to a single instance.
#app.py
from Socket_Singleton import Socket_Singleton
Socket_Singleton()
input() #Blocking call to simulate your_business_logic()
The first time app.py is launched:
>> C:\current\working\directory λ python app.py
>>
app.py executes normally. (Here, app.py blocks until we satisfy input(). Replace this with your own logic. The examples and basic recipes on this page contain these calls simply for demonstration purposes.)
Now, in another shell, if we try:
>> C:\current\working\directory λ python app.py
>> C:\current\working\directory λ
The interpreter exits immediately and we end up back at the prompt.
We can also get access to arguments passed from subsequent attempts to run python app.py
with the arguments
property, although is not intended to be accessed directly- it's most likely more convenient to use the trace()
method. This allows you to register a callback, which gets called when arguments
is appended (as other instances try to run).
Socket_Singleton.trace(observer, *args, **kwargs)
#app.py
from Socket_Singleton import Socket_Singleton
def callback(argument, *args, **kwargs):
print(argument)
#do_a_thing(argument)
def main():
app = Socket_Singleton()
app.trace(callback, *args, **kwargs)
input() #Blocking call to simulate your_business_logic()
if __name__ == "__main__":
main()
At the terminal:
>> C:\current\working\directory λ python app.py
>>
In another shell, subsequent attempts to python app.py
now look like this:
>> C:\current\working\directory λ python app.py foo bar baz
>> C:\current\working\directory λ
Meanwhile, our output for the original python app.py
shell looks like this:
>> C:\current\working\directory λ python app.py
>> foo
>> bar
>> baz
We can also detach a given observer / callback via untrace()
with a similar interface.
Socket_Singleton.untrace(observer)
If you'd prefer to disconnect from the port prematurely, thus releasing the "lock", there's a close()
method:
#app.py
from Socket_Singleton import Socket_Singleton
def main():
app = Socket_Singleton()
app.close()
print("Running!")
input()
if __name__ == "__main__":
main()
At the terminal:
>> C:\current\working\directory λ python app.py
>> Running!
>>
And in a new shell:
>> C:\current\working\directory λ python app.py
>> Running!
>>
Context manager protocol is implemented as well:
with Socket_Singleton():
input() #Blocking call to simulate your_business_logic()
Socket_Singleton.__enter__()
returns self so we can can have access to the object if needed:
with Socket_Singleton() as ss:
ss.trace(callback)
input() #Blocking call to simulate your_business_logic()
timeout
A duration in seconds, specifying how long to hold the socket. Defaults to 0 (no timeout, keep-alive). Countdown starts at the end of initialization, immediately after the socket is bound successfully.
clients
An integer property describing how many client processes have connected since instantiation.
max_clients
A positive integer describing how many client processes to capture before internally calling close() and releasing the port. Defaults to 0 (no maximum)
strict=False
We can raise and capture a custom exception, MultipleSingletonsError
, rather than entirely destroying the process which fails to become the singleton.
from Socket_Singleton import Socket_Singleton, MultipleSingletonsError
def callback(arg):
print(f"callback: {arg}")
def main():
try:
app = Socket_Singleton(strict=False)
except MultipleSingletonsError as err:
print("We are not the singleton.")
print(err)
else:
print("We are the singleton!")
app.trace(callback)
[print(arg) for arg in app.arguments]
# print(app)
# print(repr(app))
# help(app)
input()
if __name__ == "__main__":
main()
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file Socket_Singleton-1.0.0.tar.gz
.
File metadata
- Download URL: Socket_Singleton-1.0.0.tar.gz
- Upload date:
- Size: 5.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1b0ebda25fd051304ab773c7e93e7a1af4dd2d54dde366fce0f4567e47d1aab8 |
|
MD5 | 134181c1a3e2896b9d80beedcb4d59db |
|
BLAKE2b-256 | 7b6fc8e20638cd312438618b9832c50e5b251ec1b599ea3e6568545b5e42d2b5 |
File details
Details for the file Socket_Singleton-1.0.0-py3-none-any.whl
.
File metadata
- Download URL: Socket_Singleton-1.0.0-py3-none-any.whl
- Upload date:
- Size: 6.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.51.0 CPython/3.9.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d0eff5a7d9b579ecafceb7bd01cb2c64971817c62ac87562914750f60f64f3ff |
|
MD5 | 427b89a6a05699f4c363540dc292112e |
|
BLAKE2b-256 | 619d6416fe35fae816937d6ed4b0fffc7c7d29e34ce8349db9f90c50834f3b5b |