Skip to main content

Atomic lock-free primitives

Project description

atomics

This library implements a wrapper around the lower level patomic C library (which is provided as part of this library through the build_patomic command in setup.py).

It exposes hardware level lock-free (and address-free) atomic operations on a memory buffer, either internally allocated or externally provided, via a set of atomic classes.

The operations in these classes are both thread-safe and process-safe, meaning that they can be used on a shared memory buffer for interprocess communication (including with other languages such as C/C++).

Table of Contents

Installing

Linux/MacOS:

$ python3 -m pip install atomics

Windows:

$ py -m pip install atomics

This library requires Python3.6+, and has a dependency on the cffi library. While the code here has no dependency on any implementation specific features, the cffi library functions used are likely to not work outside of CPython and PyPy.

Binaries are provided for the following platforms:

  • Windows [x86, amd64]
  • MacOSX [x86_64, universal2]
  • Linux [i686, x86_64, aarch64, ppc64le, s390x] [manylinux2014, musllinux_1_1]
  • Linux [i686, x86_64] [manylinux1]

If you are on one of these platforms and pip tries to build from source or fails to install, make sure that you have the latest version of pip installed. This can be done like so:

Linux/MacOS:

$ python3 -m pip install --upgrade pip

Windows:

$ py -m pip install --upgrade pip

If you need to build from source, check out the Building section as there are additional requirements for that.

Examples

Incorrect

The following example has a data race (ais modified from multiple threads). The program is not correct, and a's value will not equal total at the end.

from threading import Thread


a = 0


def fn(n: int) -> None:
    global a
    for _ in range(n):
        a += 1


if __name__ == "__main__":
    # setup
    total = 10_000_000
    # run threads to completion
    t1 = Thread(target=fn, args=(total // 2,))
    t2 = Thread(target=fn, args=(total // 2,))
    t1.start(), t2.start()
    t1.join(), t2.join()
    # print results
    print(f"a[{a}] != total[{total}]")

Multi-Threading

This example implements the previous example but a is now an AtomicInt which can be safely modified from multiple threads (as opposed to int which can't). The program is correct, and a will equal total at the end.

import atomics
from threading import Thread


def fn(ai: atomics.INTEGRAL, n: int) -> None:
    for _ in range(n):
        ai.inc()


if __name__ == "__main__":
    # setup
    a = atomics.atomic(width=4, atype=atomics.INT)
    total = 10_000
    # run threads to completion
    t1 = Thread(target=fn, args=(a, total // 2))
    t2 = Thread(target=fn, args=(a, total // 2))
    t1.start(), t2.start()
    t1.join(), t2.join()
    # print results
    print(f"a[{a.load()}] == total[{total}]")

Multi-Processing

This example is the counterpart to the above correct code, but using processes to demonstrate that atomic operations are also safe across processes. This program is also correct, and a will equal total at the end. It is also how one might communicate with processes written in other languages such as C/C++.

import atomics
from multiprocessing import Process, shared_memory


def fn(shmem_name: str, width: int, n: int) -> None:
    shmem = shared_memory.SharedMemory(name=shmem_name)
    buf = shmem.buf[:width]
    with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
        for _ in range(n):
            a.inc()
    del buf
    shmem.close()


if __name__ == "__main__":
    # setup
    width = 4
    shmem = shared_memory.SharedMemory(create=True, size=width)
    buf = shmem.buf[:width]
    total = 10_000
    # run processes to completion
    p1 = Process(target=fn, args=(shmem.name, width, total // 2))
    p2 = Process(target=fn, args=(shmem.name, width, total // 2))
    p1.start(), p2.start()
    p1.join(), p2.join()
    # print results and cleanup
    with atomics.atomicview(buffer=buf, atype=atomics.INT) as a:
        print(f"a[{a.load()}] == total[{total}]")
    del buf
    shmem.close()
    shmem.unlink()

NOTE: Although shared_memory is showcased here, atomicview accepts any type that supports the buffer protocol as its buffer argument, so other sources of shared memory such as mmap could be used instead.

Docs

Types

The following helper (abstract-ish base) types are available in atomics:

  • [ANY, INTEGRAL, BYTES, INT, UINT]

This library provides the following Atomic classes in atomics.base:

  • Atomic --- ANY
  • AtomicIntegral --- INTEGRAL
  • AtomicBytes --- BYTES
  • AtomicInt --- INT
  • AtomicUint --- UINT

These Atomic classes are constructable on their own, but it is strongly suggested using the atomic() function to construct them. Each class corresponds to one of the above helper types (as indicated).

This library also provides Atomic*View (in atomics.view) and Atomic*ViewContext (in atomics.ctx) counterparts to the Atomic* classes, corresponding to the same helper types.

The latter of the two sets of classes can be constructed manually, although it is strongly suggested using the atomicview() function to construct them. The former set of classes cannot be constructed manually with the available types, and should only be obtained by called .__enter__() on a corresponding Atomic*ViewContext object.

Even though you should never need to directly use these classes (apart from the helper types), they are provided to be used in type hinting. The inheritance hierarchies are detailed in the ARCHITECTURE.md file (available on GitHub).

Construction

This library provides the functions atomic and atomicview, along with the types BYTES, INT, and UINT (as well as ANY and INTEGRAL) to construct atomic objects like so:

import atomics

a = atomics.atomic(width=4, atype=atomics.INT)
print(a)  # AtomicInt(value=0, width=4, readonly=False, signed=True)

buf = bytearray(2)
with atomics.atomicview(buffer=buf, atype=atomics.BYTES) as a:
    print(a)  # AtomicBytesView(value=b'\x00\x00', width=2, readonly=True)

You should only need to construct objects with an atype of BYTES, INT, or UINT. Using an atype of ANY or INTGERAL will require additional kwargs, and an atype of ANY will result in an object that doesn't actually expose any atomic operations (only properties, explained in sections further on).

The atomic() function returns a corresponding Atomic* object.

The atomicview() function returns a corresponding Atomic*ViewContext object. You can use this context object in a with statement to obtain an Atomic*View object. The buffer parameter may be any object that supports the buffer protocol.

Construction can raise UnsupportedWidthException and AlignmentError.

NOTE: the width property of Atomic*View objects is derived from the buffer's length as if it were contiguous. It is equivalent to calling memoryview(buf).nbytes.

Lifetime

Objects of Atomic* classes (i.e. objects returned by the atomic() function) have a self-contained buffer which is automatically freed. They can be passed around and stored liked regular variables, and there is nothing special about their lifetime.

Objects of Atomic*ViewContext classes (i.e. objects returned by the atomicview() function) and Atomic*View objects obtained from said objects have a much stricter usage contract.

Contract

The buffer used to construct an Atomic*ViewContext object (either directly or through atomicview()) MUST NOT be invalidated until .release() is called. This is aided by the fact that .release() is called automatically in .__exit__(...) and .__del__(). As long as you immediately use the context object in a with statement, and DO NOT invalidate the buffer inside that with scope, you will always be safe.

The protections implemented are shown in this example:

import atomics


buf = bytearray(4)
ctx = atomics.atomicview(buffer=buf, atype=atomics.INT)

# ctx.release() here will cause ctx.__enter__() to raise:
# ValueError("Cannot open context after calling 'release'.")

with ctx as a:  # this calls ctx.__enter__()
    # ctx.release() here will raise:
    # ValueError("Cannot call 'release' while context is open.")

    # ctx.__enter__() here will raise:
    # ValueError("Cannot open context multiple times.")
    
    print(a.load())  # ok

# ctx.__exit__(...) now called
# we can safely invalidate object 'buf' now

# ctx.__enter__() will raise:
# ValueError("Cannot open context after calling 'release'.")

# accessing object 'a' in any way will also raise an exception

Furthermore, in CPython, all built-in types supporting the buffer protocol will throw a BufferError exception if you try to invalidate them while they're in use (i.e. before calling .release()).

As a last resort, if you absolutely must invalidate the buffer inside the with context (where you can't call .release()), you may call .__exit__(...) manually on the Atomic*ViewContext object. This is to force explicitness about something considered to be bad practice and dangerous.

Where it's allowed, .release() may be called multiple times with no ill-effects. This also applies to .__exit__(...), which has no restrictions on where it can be called.

Alignment

Different platforms may each have their own alignment requirements for atomic operations of given widths. This library provides the Alignment class in atomics to ensure that a given buffer meets these requirements.

from atomics import Alignment

buf = bytearray(8)
align = Alignment(len(buf))
assert align.is_valid(buf)

If an atomic class is constructed from a misaligned buffer, the constructor will raise AlignmentError.

By default, .is_valid calls .is_valid_recommended. The class Alignment also exposes .is_valid_minimum. Currently, no atomic class makes use of the minimum alignment, so checking for it is pointless. Support for it will be added in a future release.

Properties

All Atomic* and Atomic*View classes have the following properties:

  • width: width in bytes of the underlying buffer (as if it were contiguous)
  • readonly: whether the object supports modifying operations
  • ops_supported: a sorted list of OpType enum values representing which operations are supported on the object

Integral Atomic* and Atomic*View classes also have the following property:

  • signed: whether arithmetic operations are signed or unsigned

In both cases, the behaviour on overflow is defined to wraparound.

Operations

Base Atomic and AtomicView objects (corresponding to ANY) expose no atomic operations.

AtomicBytes and AtomicBytesView objects support the following operations:

  • [base]: load, store
  • [xchg]: exchange, cmpxchg_weak, cmpxchg_strong
  • [bitwise]: bit_test, bit_compl, bit_set, bit_reset
  • [binary]: bin_or, bin_xor, bin_and, bin_not
  • [binary]: bin_fetch_or, bin_fetch_xor, bin_fetch_and, bin_fetch_not

Integral Atomic* and Atomic*View classes additionally support the following operations:

  • [arithmetic]: add, sub, inc, dec, neg
  • [arithmetic]: fetch_add, fetch_sub, fetch_inc, fetch_dec, fetch_neg

The usage of (most of) these functions is modelled directly on the C++11 std::atomic implementation found here.

Compare Exchange (cmpxchg_*)

The cmpxchg_* functions return CmpxchgResult. This has the attributes .success: bool which indicates whether the exchange took place, and .expected: T which holds the original value of the atomic object.
The cmpxchg_weak function may fail spuriously, even if expected matches the actual value. It should be used as shown below:

import atomics


def atomic_mul(a: atomics.INTEGRAL, operand: int):
    res = atomics.CmpxchgResult(success=False, expected=a.load())
    while not res:
        desired = res.expected * operand
        res = a.cmpxchg_weak(expected=res.expected, desired=desired)

In a real implementation of atomic_mul, care should be taken to ensure that desired fits in a (i.e. desired.bit_length() < (a.width * 8), assuming 8 bits in a byte).

Exceptions

All operations can raise UnsupportedOperationException (so check .ops_supported if you need to be sure).

Operations load, store, and cmpxchg_* can raise MemoryOrderError if called with an invalid memory order. MemoryOrder enum values expose the functions is_valid_store_order(), is_valid_load_order(), and is_valid_fail_order() to check with.

Special Methods

AtomicBytes and AtomicBytesView implement the __bytes__ special method.

Integral Atomic* and Atomic*View classes implement the __int__ special method. They intentionally do not implement __index__.

There is a notable lack of any classes implementing special methods corresponding to atomic operations; this is intentional. Assignment in Python is not available as a special method, and we do not want to encourage people to use other special methods with this class, lest it lead to them accidentally using assignment when they meant .store(...).

Memory Order

The MemoryOrder enum class is provided in atomics, and the memory orders are directly copied from C++11's std::memory_order documentation found here, except for CONSUME (which would be pointless to expose in this library).

All operations have a default memory order, SEQ_CST. This will enforce sequential consistency, and essentially make your multi-threaded and/or multi-processed program be as correct as if it were to run in a single thread.

IF YOU DO NOT UNDERSTAND THE LINKED DOCUMENTATION, DO NOT USE YOUR OWN MEMORY ORDERS!!! Stick with the defaults to be safe. (And realistically, this is Python, you won't get a noticeable performance boost from using a more lax memory order).

The following helper functions are provided:

  • .is_valid_store_order() (for store op)
  • .is_valid_load_order() ( for load op)
  • .is_valid_fail_order() (for the fail ordering in cmpxchg_* ops)

Passing an invalid memory order to one of these ops will raise MemoryOrderError.

Exceptions

The following exceptions are available in atomics.exc:

  • AlignmentError
  • MemoryOrderError
  • UnsupportedWidthException
  • UnsupportedOperationException

Building

IMPORTANT: Make sure you have the latest version of pip installed.

Using setup.py's build or bdist_wheel commands will run the build_patomic command (which you can also run directly).

This clones the patomic library into a temporary directory, builds it, and then copies the shared library into atomics._clib.

This requires that git be installed on your system (a requirement of the GitPython module). You will also need an ANSI/C90 compliant C compiler (although ideally a more recent compiler should be used). CMake is also required but should be automatically pip install'd if not available.

If you absolutely cannot get build_patomic to work, go to patomic, follow the instructions on building it (making sure to build the shared library version), and then copy-paste the shared library file into atomics._clib manually.

NOTE: Currently, the library builds a dummy extension in order to trick setuptools into building a non-purepython wheel. If you are ok with a purepython wheel, then feel free to remove the code for that from setup.py (at the bottom).
Otherwise, you will need a C99 compliant C compiler, and probably the development libraries/headers for whichever version of Python you're using.

Future Thoughts

  • add docstrings
  • add tests
  • add support for minimum alignment
  • add support for constructing Atomic classes' buffers in shared memory
  • add support for passing Atomic objects to sub-processes and sub-interpreters
  • reimplement in C or Cython for performance gains (preliminary benchmarks put such implementations at 2x the speed of a raw int)

Contributing

I don't have a guide for contributing yet. This section is here to make the following two points:

  • new operations must first be implemented in patomic before this library can be updated
  • new architectures, widths, and existing unsupported operations must be supported in patomic (no change required in this library)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

atomics-1.0.2.tar.gz (44.2 kB view details)

Uploaded Source

Built Distributions

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win_amd64.whl (62.0 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 Windows x86-64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win32.whl (58.4 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 Windows x86

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_x86_64.whl (193.0 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 musllinux: musl 1.1+ x86-64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_s390x.whl (221.2 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 musllinux: musl 1.1+ s390x

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_ppc64le.whl (202.7 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 musllinux: musl 1.1+ ppc64le

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_i686.whl (194.0 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 musllinux: musl 1.1+ i686

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_aarch64.whl (200.7 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 musllinux: musl 1.1+ ARM64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (219.1 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 manylinux: glibc 2.17+ s390x

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (203.0 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 manylinux: glibc 2.17+ ppc64le

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (237.3 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 manylinux: glibc 2.17+ ARM64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (203.8 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 manylinux: glibc 2.12+ x86-64 manylinux: glibc 2.5+ x86-64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl (205.3 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 manylinux: glibc 2.12+ i686 manylinux: glibc 2.5+ i686

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_x86_64.whl (73.0 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 macOS 10.9+ x86-64

atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_universal2.whl (73.8 kB view details)

Uploaded Python 3.10 Python 3.11 Python 3.6 Python 3.7 Python 3.8 Python 3.9 macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file atomics-1.0.2.tar.gz.

File metadata

  • Download URL: atomics-1.0.2.tar.gz
  • Upload date:
  • Size: 44.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2.tar.gz
Algorithm Hash digest
SHA256 04141e2ed22220ca3faa2057d13f55e4a511e91b5ae8c787fa701e9320217211
MD5 8d179cb19fdc9933e9c54d7e89540b40
BLAKE2b-256 cf15317e77646da3b316d49c93b8ea89e57c21b128e918f640c7130e7bff1c9e

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win_amd64.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win_amd64.whl
  • Upload date:
  • Size: 62.0 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win_amd64.whl
Algorithm Hash digest
SHA256 b0ba7d14a27158425b4b3d6b73d0e9f10bfcf55af2e3fb51a8727e2293ecf338
MD5 6e1d9cc280a7b516b7160c81b3ab32ef
BLAKE2b-256 c108c02d9c2cacfe0b254a222eeb1b827929f89de754a3ce69d1d3cc4a3703b4

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win32.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win32.whl
  • Upload date:
  • Size: 58.4 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-win32.whl
Algorithm Hash digest
SHA256 aa4ee4ff8e4b3f445fe0640f3f2ccfaa61ce099d7cc03415d96d2d7ae05b7a0b
MD5 2ffae0a7a19c994b053b35ee813df89b
BLAKE2b-256 b8a3aa503779b1a42aad73518b9fca89581e7fc6c8713585205c0d7fb315b6fc

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_x86_64.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_x86_64.whl
  • Upload date:
  • Size: 193.0 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, musllinux: musl 1.1+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_x86_64.whl
Algorithm Hash digest
SHA256 58ecf69d4b67d3792d8bf0ae076f109d8c2cb4185c8d8a62cc672a74d80a09cc
MD5 9d814a33040080de657c39ef9a9a9304
BLAKE2b-256 2328b72c195f4199899d5aab2230ffbfdc2abf6d73f3810cba948a6187408509

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_s390x.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_s390x.whl
  • Upload date:
  • Size: 221.2 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, musllinux: musl 1.1+ s390x
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_s390x.whl
Algorithm Hash digest
SHA256 c9518fc7255103625444af7eed7a0643f5d93c7ea974a063893f3c4db62e6571
MD5 610dbc9d734718507e5525ed30ee07d7
BLAKE2b-256 da6308edcae0d1334a7523db613f2c4eff1d34aeab14dbd3aeb39f262f5a5bb9

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_ppc64le.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_ppc64le.whl
  • Upload date:
  • Size: 202.7 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, musllinux: musl 1.1+ ppc64le
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_ppc64le.whl
Algorithm Hash digest
SHA256 ac852fa341bf7562a809176701af0825f449badfd20a1ff2dd6de84fc45a3160
MD5 619e256464a40840079c3332eda0f7d5
BLAKE2b-256 1132f7bb1e0a3ffb92e7f1788a48c4d600c680fdda002ad1a4fbf0035c4c7f17

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_i686.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_i686.whl
  • Upload date:
  • Size: 194.0 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, musllinux: musl 1.1+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_i686.whl
Algorithm Hash digest
SHA256 ca823e5dfbcda305e6084ef99f1a4fab88dda3995378212985cfe5f0a9c8dafa
MD5 01a8ee05018bb7983af6201f170a86f3
BLAKE2b-256 e92d4cc6c215c0ee38f340341377ce340ee6da4a9f9e809a30876e6c2ce881eb

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_aarch64.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_aarch64.whl
  • Upload date:
  • Size: 200.7 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, musllinux: musl 1.1+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-musllinux_1_1_aarch64.whl
Algorithm Hash digest
SHA256 123df222d052308257de3b86a9f7400322483955b398c7464898ce0ecd48bf3d
MD5 37448fab9d36d9bfd6fa7ee1b33c1a63
BLAKE2b-256 ee50134f776bca6bb4732b9273101b8b66d5e2fa0fbf2a18310abb4b25923557

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_s390x.manylinux2014_s390x.whl.

File metadata

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 ac1a677e6fd41200951ad6116f23d27285697da05fb622bf669ab7d80d17d0dc
MD5 3e1f397f3425a9ba11db7857d4c52cc4
BLAKE2b-256 d8b892a67692288f781ea8250f703a54a2340756b61c32aa72e5e273abb09076

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.

File metadata

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 a57621b58a881be07c3b0eb3dd34cfcf1a7e9ec84f8f1cffaaa3682a3f6fde65
MD5 77c180ecde14988bdbd24ecb2bb54e24
BLAKE2b-256 002559212d816c647f2752de85ae8fd0e4b4790176380c70f949ebe409f860cd

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ee035424d0c75d72c2ddc53172ddee37259cbe397c9847868601b509fc8f6ea0
MD5 5e3ae14afecea5cd07106b58fa7b97d9
BLAKE2b-256 7a996cb9f45707ebee5f23b7869faea8b4a432bd7736c43e8f162bc6c8321d5c

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl.

File metadata

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl
Algorithm Hash digest
SHA256 6c2ee992777c03ca3530c9f4a2d78a88c65f564a511c9698d42905f57a04344b
MD5 8eef396aaceec6477dfc899ecda09b2a
BLAKE2b-256 1ce4c0b3d45d7bb35068ef7e7716d6cc2eb48dc102c8564ca1166612af7a9353

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl.

File metadata

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl
Algorithm Hash digest
SHA256 d50afc7ca07f59b6cb38aea2d0eb4f7ea4ef6a79184a1f30309699c149a7c2c4
MD5 e591335ddace03d9d90c44caea4e176c
BLAKE2b-256 44837c64fef8daf4ac2ae26837f24193cc9f0b2e52f1bed54208c36d969c067a

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 73.0 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, macOS 10.9+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 947b43a4958ed5c9ecfd21793d849cc2869ef003ff380432e01356e6e5b2e55a
MD5 76b152828a6e043d63d242ab3cfb712d
BLAKE2b-256 5a31f7419def42c532150e4f49f457cdbf738aef576b6883b03f1e3b7e87c928

See more details on using hashes here.

File details

Details for the file atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_universal2.whl.

File metadata

  • Download URL: atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_universal2.whl
  • Upload date:
  • Size: 73.8 kB
  • Tags: Python 3.10, Python 3.11, Python 3.6, Python 3.7, Python 3.8, Python 3.9, macOS 10.9+ universal2 (ARM64, x86-64)
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.9

File hashes

Hashes for atomics-1.0.2-py36.py37.py38.py39.py310.py311-none-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 5bba08c8ed9dff560a7ce0285b00d72731700e64cb22ce8f9636c88c6dce4d74
MD5 a6f5b7e6e9ad05270467e6c0b8725dff
BLAKE2b-256 80fe8965740c8fbe8760363c94261ff08db1d1402ff2c44fc7e4189311efdd45

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page