Skip to main content

Python bindings for pkcore, a high-performance poker analysis library

Project description

CI PyPI License: GPL v3

pkpy

Python bindings for pkcore, a high-performance poker analysis library written in Rust.

What This Project Does

pkpy lets Python developers use pkcore's poker engine — card parsing, hand evaluation, Texas Hold'em game simulation, outs calculation, and more — without writing any Rust. The Rust library runs natively and is called directly from Python with no subprocess overhead or serialization round-trips.


Dependencies

  • pkcore — the underlying Rust poker analysis library
  • PyO3 — Rust/Python bindings framework
  • Maturin — build tool for PyO3 extension modules

See docs/STACK.md for more details on the technology stack.


Cactus Kev Binary Card Representation

pkcore represents each card as a single u32 using a variation of Cactus Kev's binary encoding, designed for O(1) hand evaluation via lookup tables.

+--------+--------+--------+--------+
|mmmbbbbb|bbbbbbbb|SHDCrrrr|xxpppppp|
+--------+--------+--------+--------+
Bits Meaning
p (6 bits) Prime number for the rank (Deuce=2, Trey=3, ..., Ace=41)
r (4 bits) Rank index (Deuce=0, Trey=1, ..., Ace=12)
SHDC (4 bits) Suit flags — one bit per suit
b (13 bits) One bit set per rank — used for flush/straight detection
m (3 bits) Frequency flags (paired, tripped, quaded) — stripped during eval

This encoding makes many operations branch-free bit manipulations. For example, detecting a flush is a single bitwise AND across five cards' suit bits.

Hand Evaluation

pkcore uses a two-level lookup table strategy (the same approach as the original Cactus Kev evaluator):

  1. Flushes and straights are detected via the rank-bit field (b bits). A 13-bit mask uniquely identifies every possible straight and flush pattern.
  2. All other hands are identified by multiplying the five rank primes together. Since every rank maps to a distinct prime, the product uniquely identifies the rank multiset — pairs, trips, quads, and full houses all have unique products. The product indexes into a lookup table that returns the HandRankValue.

A lower HandRankValue is a stronger hand (1 = royal flush, 7462 = worst high card).


Project Structure

pkpy/
├── Cargo.toml              # Rust crate manifest
├── pyproject.toml          # Python build config (maturin)
├── src/
│   └── lib.rs              # All PyO3 bindings
├── python/
│   └── pkpy/
│       └── __init__.py     # Python package — re-exports everything from the extension
└── tests/
    └── test_pkpy.py        # pytest test suite

The python/pkpy/ directory is the Python package. The compiled Rust extension (_pkpy.so) is dropped into it by maturin. __init__.py re-exports everything so users write from pkpy import Card rather than from pkpy._pkpy import Card.


API Reference

Card

A single playing card. Internally a u32 in Cactus Kev format.

from pkpy import Card, Rank, Suit

# Parse from string — accepts "As", "A♠", "a♠", "AH", etc.
ace_spades = Card.parse("As")
king_hearts = Card.parse("K♥")

# Construct from rank and suit
card = Card.from_rank_suit(Rank.QUEEN, Suit.DIAMONDS)

# Inspect
card.rank()              # -> Rank
card.suit()              # -> Suit
card.is_dealt()          # -> bool (False for blank/sentinel cards)
card.as_u32()            # -> int (raw Cactus Kev encoding)
card.bit_string()        # -> str (binary representation of the encoding)
card.get_rank_prime()    # -> int (rank prime used in hand evaluation)
card.get_letter_index()  # -> str (letter-index form, e.g. "As")

str(card)                # -> "Q♦"
card == Card.parse("Qd") # -> True

Deck

A standard 52-card deck. All methods are static — Deck is a namespace for deck-level operations.

from pkpy import Deck

deck = Deck.poker_cards()           # -> Cards, ordered A♠ down to 2♣
shuffled = Deck.poker_cards_shuffled()  # -> Cards, randomly shuffled
Deck.get(0)                         # -> Card (A♠, the first card in deck order)
Deck.len()                          # -> 52

Cards

An ordered, unique collection of cards backed by an IndexSet (ordered hash set). Duplicate inserts are silently ignored.

from pkpy import Cards

hand = Cards.parse("As Ks Qh")
deck = Cards.deck()           # full 52-card deck in order

len(hand)                         # -> 3
hand.is_empty()                   # -> False
hand.contains(Card.parse("As"))   # -> True
hand.remaining()                  # -> Cards with 49 cards (deck minus hand)
hand.remaining_after(board)       # -> deck minus hand minus board
hand.is_dealt()                   # -> True if no blank cards
hand.are_unique()                 # -> True if no duplicates

for card in hand:                 # iterable
    print(card)

hand.to_list()                    # -> list[Card]
hand.get_index(0)                 # -> Card | None (card at position 0)

# Mutation
hand.insert(Card.parse("Jh"))     # -> bool (True if card was new)
hand.remove(Card.parse("As"))     # -> bool (True if card was present)
hand.append(Cards.parse("Tc 9c")) # merge another Cards in place
hand.shuffle_in_place()           # shuffle in place

# Non-mutating transformations
hand.shuffle()                    # -> Cards (shuffled copy)
hand.sort()                       # -> Cards (sorted highest rank first)
hand.minus(other)                 # -> Cards (this minus other)
hand.filter_by_suit(Suit.SPADES)  # -> Cards (only spades)
hand.combinations(2)              # -> list[Cards] (all 2-card combos)

# Drawing (mutates the source collection)
card = hand.draw_one()            # -> Card (removes and returns the top card)
drawn = hand.draw(3)              # -> Cards (removes and returns 3 cards)
rest = hand.draw_all()            # -> Cards (empties the collection)

# Deck-relative operations
hand.deck_minus()                 # -> Cards (52-card deck minus this collection)
hand.deck_primed()                # -> Cards (this collection first, then rest of deck)

HoleCards

A collection of two-card hands for one or more players. Cards are parsed in pairs: the first two belong to player 1, the next two to player 2, and so on.

from pkpy import HoleCards

# Two players
hc = HoleCards.parse("As Kh 8d Kc")
len(hc)        # -> 2
hc.is_empty()  # -> False
hc.get(0)      # -> Two | None (0-indexed)
hc.to_list()   # -> list[Two]

# Build programmatically
hc = HoleCards.parse("As Kh")
hc.push(Two.parse("Qd Jc"))
len(hc)  # -> 2

Board

The community cards (flop, turn, river).

from pkpy import Board

board = Board.parse("Ac 8h 7h 9s")      # flop + turn
board = Board.parse("Ac 8h 7h 9s 5s")  # full board

board.turn_cards()  # -> Cards (flop + turn, 4 cards)
str(board)          # -> "FLOP: A♣ 8♥ 7♥, TURN: 9♠, RIVER: _"

Game

Combines hole cards and a board. The main entry point for analysis.

from pkpy import Game, HoleCards, Board, Outs

hc    = HoleCards.parse("As Kh 8d Kc")
board = Board.parse("Ac 8h 7h 9s")
game  = Game(hc, board)

game.has_dealt_turn()          # -> bool (True if board has a turn card)
case_evals = game.turn_case_evals()          # evaluates all possible river cards
game.turn_eval_for_player(0)   # -> Eval for player at index 0 (raises on missing turn)
game.turn_remaining_board()    # -> Cards (deck cards not yet on the board or in hands)
game.flop_and_turn()           # -> Cards (the 4 board cards through the turn)

flop_eval = game.flop_eval()   # -> FlopEval | None
turn_eval = game.turn_eval()   # -> TurnEval | None

print(game.turn_nuts_display())  # best hands possible at the turn
print(game.river_display())      # final result with winner

CaseEvals

The result of game.turn_case_evals(). Contains one evaluation per possible river card (typically 44–46 entries depending on how many cards are already accounted for).

len(case_evals)  # -> number of possible river cards evaluated

Outs

Cards that, if dealt on the river, cause a specific player to win. Built from CaseEvals.

from pkpy import Outs

outs = Outs.from_case_evals(case_evals)

outs.len_for_player(1)   # -> int: number of winning river cards for player 1
outs.len_for_player(2)   # -> int: number of winning river cards for player 2
outs.get(1)              # -> Cards | None: the actual out cards for player 1
outs.longest_player()    # -> int: player id with the most outs
outs.is_longest(2)       # -> bool
outs.len_longest()       # -> int: how many outs the leading player has

Players are 1-indexed.

HandRank and HandRankClass

HandRank holds the numeric strength of a five-card hand. Lower value = stronger hand.

HandRankClass is the detailed category (e.g., RoyalFlush, FourAces, AcesOverKings).

from pkpy import HandRankClass

HandRankClass.ROYAL_FLUSH.is_straight_flush()  # -> True
str(HandRankClass.ROYAL_FLUSH)                 # -> "RoyalFlush"

HandRank is obtained from Eval objects, which come out of CaseEvals. Direct construction is not exposed since you'd normally get them via game evaluation.

Constants

from pkpy import (
    unique_5_card_hands,    # 2,598,960
    distinct_5_card_hands,  # 7,462
    unique_2_card_hands,    # 1,326
    distinct_2_card_hands,  # 169
)

GTO Range Analysis

Combo

An abstract hand combination defined by rank(s) and a suit qualifier.

from pkpy import Combo

c = Combo.parse("AKs")
c.is_suited()           # -> True
c.is_pair()             # -> False
c.is_ace_x()            # -> True
c.total_pairs()         # -> 4  (four suited AK combos)
c.first                 # -> Rank.ACE
c.second                # -> Rank.KING
c.plus                  # -> False

Combo.parse("JJ+").plus         # -> True
Combo.parse("QQ").total_pairs() # -> 6  (six ways to make QQ)
Combo.parse("AKo").total_pairs()# -> 12 (twelve offsuit AK combos)

Combos

A range of abstract hand combinations, parsed from standard poker range notation.

from pkpy import Combos

r = Combos.parse("QQ+, AK")
len(r)          # -> 5  (QQ, KK, AA, AKs, AKo as abstract combos)

twos = r.explode()
len(twos)       # -> 34 (all concrete two-card hands)

# Predefined ranges (returned as strings, pass to Combos.parse)
Combos.PERCENT_2_5   # "QQ+, AK"       — top ~2.5% of hands
Combos.PERCENT_5     # "TT+, AQ+"      — top ~5%
Combos.PERCENT_10    # "44+, AJ+, ..."  — top ~10%
Combos.PERCENT_20    # top ~20%
Combos.PERCENT_33    # top ~33%

# Parse a predefined range
tight = Combos.parse(Combos.PERCENT_2_5)

Two

A concrete two-card hand — the unit produced by combo explosion.

from pkpy import Two

t = Two.parse("As Kh")
t.first()               # -> Card (A♠)
t.second()              # -> Card (K♥)
t.is_suited()           # -> False
t.is_pair()             # -> False
t.contains_rank(Rank.ACE)   # -> True
t.contains_suit(Suit.SPADES) # -> True

Twos

The collection returned by Combos.explode(). Supports filtering.

from pkpy import Combos

twos = Combos.parse("QQ+, AK").explode()

twos.filter_is_paired()       # -> Twos  (only pocket pairs)
twos.filter_is_not_paired()   # -> Twos  (only non-paired hands)
twos.filter_is_suited()       # -> Twos  (only suited hands)
twos.filter_is_not_suited()   # -> Twos  (only offsuit hands)
twos.filter_on_rank(Rank.ACE) # -> Twos  (hands containing an Ace)
twos.filter_on_card(Card.parse("As"))  # -> Twos (hands containing A♠)

twos.to_list()    # -> list[Two]
twos.contains(Two.parse("As Kh"))  # -> bool

Qualifier

The suit qualifier for a combo: SUITED, OFFSUIT, or ALL.

from pkpy import Combo, Qualifier

Combo.parse("AKs").qualifier == Qualifier.SUITED   # -> True
Combo.parse("AKo").qualifier == Qualifier.OFFSUIT  # -> True
Combo.parse("AK").qualifier  == Qualifier.ALL      # -> True

GTO Example

from pkpy import Combos, Rank

# Villain's opening range
villain_range = Combos.parse("66+,AJs+,KQs,AJo+,KQo")

# Expand to all concrete two-card hands
twos = villain_range.explode()
print(f"Total hands in range: {len(twos)}")

# How many are pocket pairs vs. unpaired?
pairs  = twos.filter_is_paired()
unpaired = twos.filter_is_not_paired()
print(f"Pairs: {len(pairs)}, Unpaired: {len(unpaired)}")

# Hands containing an Ace
ace_hands = twos.filter_on_rank(Rank.ACE)
print(f"Ace-x hands: {len(ace_hands)}")

# Suited vs. offsuit breakdowns
print(f"Suited: {len(twos.filter_is_suited())}")
print(f"Offsuit: {len(twos.filter_is_not_suited())}")

Binary Card Maps

pkpy exposes pkcore's binary card map types, which provide compact, high-performance hand evaluation storage. These are the building blocks for precomputed lookup tables.

Bard

A 64-bit bitset where each of the 52 cards occupies one bit. Set operations (union, intersection, membership) are single CPU instructions.

from pkpy import Bard, Card, Cards

# Construct
b = Bard.from_card(Card.parse("As"))        # single card
b = Bard.from_cards(Cards.parse("As Ks"))   # from a Cards collection
b = Bard.from_u64(4_362_862_139_015_168)    # from a raw u64

# Constants
Bard.BLANK    # all bits zero
Bard.ALL      # all 52 card bits set

# Operations
b2 = b.fold_in(Card.parse("Qs"))  # returns new Bard with that card added
b.as_u64()                         # -> int  (raw bit value)
b.to_cards()                       # -> Cards  (reconstruct card set)
b.as_guided_string()               # -> str  (debug visualization)

SevenFiveBCM

A binary card map entry for a 5- or 7-card hand. Stores the hand's Bard, the best 5-card sub-hand's Bard, and the hand rank value. This is the format used by pkcore's precomputed CSV lookup table.

rank follows the Cactus Kev convention: lower is stronger (1 = royal flush, 7462 = worst high card).

from pkpy import Cards, SevenFiveBCM

# Build from a 5-card hand
bcm = SevenFiveBCM.from_cards(Cards.parse("As Ks Qs Js Ts"))
bcm.rank    # -> 1  (royal flush)
bcm.bc      # -> Bard  (bitset of the full hand)
bcm.best    # -> Bard  (bitset of the best 5-card sub-hand; same as bc for 5 cards)

# Build from a 7-card hand — bc is the full 7-card bard, best is the best 5
bcm7 = SevenFiveBCM.from_cards(Cards.parse("As Ks Qs Js Ts 9s 8s"))
bcm7.rank                     # -> 1
bcm7.bc.to_cards()            # -> Cards (7 cards)
bcm7.best.to_cards()          # -> Cards (best 5 cards)

# CSV generation (produces the ~5 GB bcm.csv lookup file — slow)
SevenFiveBCM.default_csv_path           # -> "generated/bcm.csv"
SevenFiveBCM.generate_csv("bcm.csv")   # enumerate all 5- and 7-card combos

IndexCardMap

Like SevenFiveBCM but stores card hands as human-readable display strings instead of Bard bitsets. Useful for inspectable CSV output.

from pkpy import Cards, IndexCardMap

icm = IndexCardMap.from_cards(Cards.parse("As Ks Qs Js Ts"))
icm.rank     # -> 1
icm.cards    # -> "A♠ K♠ Q♠ J♠ T♠"
icm.best     # -> "A♠ K♠ Q♠ J♠ T♠"  (same for 5-card hand)

icm7 = IndexCardMap.from_cards(Cards.parse("As Ks Qs Js Ts 9s 8s"))
icm7.cards   # -> "A♠ K♠ Q♠ J♠ T♠ 9♠ 8♠"  (all 7 cards)
icm7.best    # -> "A♠ K♠ Q♠ J♠ T♠"          (best 5)

IndexCardMap.generate_csv("icm.csv")

BCM example

from pkpy import Cards, SevenFiveBCM, IndexCardMap

hands = [
    Cards.parse("As Ks Qs Js Ts"),      # royal flush
    Cards.parse("As Ks Qs Js 9s"),      # king-high straight flush
    Cards.parse("As Ad Ah Ac Ks"),      # four aces
]

for hand in hands:
    bcm = SevenFiveBCM.from_cards(hand)
    icm = IndexCardMap.from_cards(hand)
    print(f"{icm.cards}  rank={bcm.rank}  best={icm.best}")

Pluribus Log Parsing

pkpy can parse hand histories from the Pluribus AI poker logs. Each line in a log file is a STATE record encoding one hand.

Log format

STATE:{index}:{rounds}:{cards}:{winnings}:{players}
  • rounds — slash-separated betting round strings, e.g. r200ffcfc/cr850cf. Each character is f (fold), c (call), or r{n} (raise to n).
  • cards — pipe-separated two-card hands, optionally followed by /board, e.g. Qc4h|Tc9c|5h5d/3h7s5c/Qs/6c.
  • winnings — pipe-separated signed integers, one per player.
  • players — pipe-separated player names.

PluribusEvent

A single action: fold, call, or raise.

from pkpy import Pluribus

hand = Pluribus.parse("STATE:0:ffr225fff:3c9s|6d5s|9dTs|2sQs|AdKd|7cTc:-50|-100|0|0|150|0:MrWhite|Gogo|Budd|Eddie|Bill|Pluribus")

for event in hand.actions():
    print(event)              # "Fold", "Call", "Raise(225)", etc.
    event.is_fold()           # -> bool
    event.is_call()           # -> bool
    event.is_raise()          # -> bool
    event.raise_amount()      # -> int | None

Pluribus

A parsed hand record.

from pkpy import Pluribus

# Parse a single log line
hand = Pluribus.parse("STATE:27:r200ffcfc/cr850cf/cr1825r3775c/r10000c:Qc4h|Tc9c|8sAs|Qh7c|JcQd|5h5d/3h7s5c/Qs/6c:-50|-200|-10000|0|0|10250:Eddie|Bill|Pluribus|MrWhite|Gogo|Budd")

hand.index           # -> 27
hand.players         # -> ['Eddie', 'Bill', 'Pluribus', 'MrWhite', 'Gogo', 'Budd']
hand.winnings        # -> [-50, -200, -10000, 0, 0, 10250]
hand.hole_cards      # -> HoleCards (6 players' hands)
hand.board           # -> Board (3h 7s 5c Qs 6c)
hand.raw             # -> the original log line string

hand.rounds()                 # -> list[str]  raw round strings
hand.actions()                # -> list[PluribusEvent]  all actions flat
hand.actions_for_round(0)     # -> list[PluribusEvent]  actions in round 0
hand.display_results()        # -> str  formatted winnings summary

# Parse an entire log file — invalid lines are silently skipped
hands = Pluribus.read_log("/path/to/pluribus.log")
print(f"Loaded {len(hands)} hands")

Pluribus example

from pkpy import Pluribus

LOG_LINE = "STATE:27:r200ffcfc/cr850cf/cr1825r3775c/r10000c:Qc4h|Tc9c|8sAs|Qh7c|JcQd|5h5d/3h7s5c/Qs/6c:-50|-200|-10000|0|0|10250:Eddie|Bill|Pluribus|MrWhite|Gogo|Budd"

hand = Pluribus.parse(LOG_LINE)

print(f"Hand #{hand.index}")
print(f"Players: {', '.join(hand.players)}")
print(f"Board: {hand.board}")
print(f"Hole cards dealt: {len(hand.hole_cards)} players")

raises = [e for e in hand.actions() if e.is_raise()]
print(f"Raises this hand: {len(raises)}")
for r in raises:
    print(f"  {r.raise_amount()}")

print(hand.display_results())

Casino Table Simulation

pkpy exposes pkcore's casino table simulation layer, which models a heads-up or multi-player poker table with blinds, betting, and chip accounting. The key types are Dealer (the engine), Player, ForcedBets, and the log/result types.

ForcedBets

Configures the blinds and optional ante for a hand.

from pkpy import ForcedBets

bets = ForcedBets(small_blind=50, big_blind=100)
bets = ForcedBets(small_blind=50, big_blind=100, ante=25)

Stack

A chip count wrapper.

from pkpy import Stack

s = Stack(1000)
s.count()     # -> 1000
s.is_empty()  # -> False

Player

A player seated at the table with a name and chip stack.

from pkpy import Player

p = Player("Alice", 1000)
p.handle          # -> "Alice"
p.chips()         # -> 1000  (current stack, excluding chips committed to pot)
p.total_chips()   # -> 1000  (chips + any committed amount)
p.state()         # -> PlayerState
p.is_active()     # -> bool
p.is_folded()     # -> bool
p.is_all_in()     # -> bool
p.is_sitting_out()# -> bool

PlayerState

Describes what a player is currently doing at the table.

state = player.state()
state.kind()      # -> str  ("Active", "Folded", "AllIn", "SittingOut")
state.amount()    # -> int  (chips committed in current state, e.g. blind amount)
state.is_active()     # -> bool
state.is_folded()     # -> bool
state.is_all_in()     # -> bool
state.is_sitting_out()# -> bool

Seatbit

A compact bitset of occupied seat numbers (seats 0–15).

from pkpy import Seatbit

sb = dealer.ready()
sb.contains(0)   # -> bool  (is seat 0 occupied?)
sb.count()       # -> int   (number of occupied seats)
sb.as_u16()      # -> int   (raw bitset value)

SeatEquity

Chip allocation tied to a set of seats — used inside Win to record who wins what.

se = win.equity
se.chips         # -> int   (chip amount)
se.seats         # -> Seatbit
se.count()       # -> int   (number of winning seats)
se.is_nada()     # -> bool  (True if chips == 0)

Win

One entry in a Winnings result. Pairs an equity award with the Eval that justified it.

win = winnings.first()
win.equity   # -> SeatEquity
win.eval     # -> Eval

Winnings

The payout result returned by Dealer.end_hand().

winnings = dealer.end_hand()
len(winnings)           # -> int  (number of pots/side-pots awarded)
winnings.first()        # -> Win  (main pot winner)
winnings.to_list()      # -> list[Win]

TableAction

A single event recorded in the table log.

action = log.last()
action.kind()    # -> str  ("Bet", "Raise", "Call", "Check", "Fold", "PostBlind", etc.)
action.seat()    # -> int  (seat number that took the action)
action.amount()  # -> int  (chip amount, 0 for non-chip actions like fold/check)

TableLog

A running record of all actions taken during the hand.

log = dealer.event_log()
log.entries()              # -> list[TableAction]  (all recorded events)
log.last()                 # -> TableAction | None
log.last_player_action()   # -> TableAction | None  (last non-system action)
log.have_posted_blinds()   # -> bool

Dealer

The table engine. Manages seating, hand flow, betting, and chip accounting.

from pkpy import Dealer, ForcedBets, Player

dealer = Dealer(ForcedBets(50, 100))

# Seat players — consumes the Player object (ownership transfer)
seat0 = dealer.seat_player(alice)   # -> int  (assigned seat number)
seat1 = dealer.seat_player(bob)

# Hand lifecycle
dealer.start_hand()           # post blinds, deal hole cards
dealer.advance_street()       # deal flop / turn / river
winnings = dealer.end_hand()  # showdown, chip transfer

# Betting actions (seat is the acting seat number)
dealer.bet(seat, amount)
dealer.call(seat)
dealer.check(seat)
dealer.raise_to(seat, amount)
dealer.all_in(seat)
dealer.fold(seat)

# State queries
dealer.ready()           # -> Seatbit  (seats with players ready to play)
dealer.next_to_act()     # -> int | None  (seat that must act next)
dealer.pot()             # -> int  (current pot total)
dealer.chips_at(seat)    # -> int  (chip count at a seat, 0 if empty)
dealer.event_log()       # -> TableLog

Casino example

from pkpy import Dealer, ForcedBets, Player, Winnings

# Set up a heads-up table: 50/100 blinds
dealer = Dealer(ForcedBets(50, 100))

alice = Player("Alice", 1000)
bob   = Player("Bob",   1000)

s_alice = dealer.seat_player(alice)
s_bob   = dealer.seat_player(bob)

# Start the hand — posts blinds, deals hole cards
dealer.start_hand()

print(f"Pot after blinds: {dealer.pot()}")      # -> 0 (blinds not in pot yet)
print(f"Next to act: {dealer.next_to_act()}")   # -> seat of first actor

# Simple action: big blind checks, small blind raises, BB calls
acting = dealer.next_to_act()
dealer.call(acting)                             # SB calls
acting = dealer.next_to_act()
dealer.check(acting)                            # BB checks

# Deal the flop, turn, river
dealer.advance_street()   # flop
dealer.advance_street()   # turn
dealer.advance_street()   # river

# Showdown
winnings = dealer.end_hand()
winner = winnings.first()
print(f"Pot won: {winner.equity.chips}")
print(f"Winning seat(s): {winner.equity.seats.as_u16()}")

# Inspect the action log
for action in dealer.event_log().entries():
    print(f"  seat {action.seat()}: {action.kind()} {action.amount() or ''}")

Complete Example

from pkpy import HoleCards, Board, Game, Outs

# Recreate the famous Negreanu vs Hansen hand:
# Daniel holds 6♠ 6♥, Gus holds 5♦ 5♣
# Flop: 9♣ 6♦ 5♥ — Daniel flops top set, Gus flops bottom set
# Turn: 5♠ — Gus rivers quads. What are the outs for each player?

hc    = HoleCards.parse("6s 6h 5d 5c")
board = Board.parse("9c 6d 5h 5s")
game  = Game(hc, board)

outs = Outs.from_case_evals(game.turn_case_evals())

print(f"Player 1 (Daniel, 6♠6♥) outs: {outs.len_for_player(1)}")
print(f"Player 2 (Gus,    5♦5♣) outs: {outs.len_for_player(2)}")
print(f"Leading player: {outs.longest_player()}")

Development Setup

Prerequisites: Rust toolchain (rustup), Python 3.8+

# Clone and enter the project
git clone <repo-url> pkpy
cd pkpy

# Create a virtual environment
python3 -m venv .venv
source .venv/bin/activate       # Windows: .venv\Scripts\activate

# Install build and test tools
pip install maturin pytest

# Compile the Rust extension and install it into the venv
python3 -m maturin develop

# Run tests
pytest

After changing src/lib.rs, re-run python3 -m maturin develop to recompile. Only the Rust source is recompiled on subsequent runs — Cargo's incremental compilation keeps this fast.

Building a Release Wheel

python3 -m maturin build --release
# Wheel lands in target/wheels/pkpy-*.whl
pip install target/wheels/pkpy-*.whl

For distribution, maturin can also publish directly to PyPI:

python3 -m maturin publish

Relationship to pkcore

This project wraps pkcore as a versioned crates.io dependency. The wrapper exposes the analysis-focused surface most useful from Python: card/deck primitives, hand evaluation, outs calculation, GTO range analysis, heads-up equity, binary card maps, Pluribus log parsing, and casino table simulation. Lower-level types (SQLite storage) are not exposed.


License

GPL-3.0-or-later, matching pkcore.

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

pkpython-0.0.54.tar.gz (121.2 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64

pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp314-cp314-win_amd64.whl (5.8 MB view details)

Uploaded CPython 3.14Windows x86-64

pkpython-0.0.54-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp314-cp314-macosx_11_0_arm64.whl (5.9 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

pkpython-0.0.54-cp314-cp314-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

pkpython-0.0.54-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.13tmanylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp313-cp313-win_amd64.whl (5.8 MB view details)

Uploaded CPython 3.13Windows x86-64

pkpython-0.0.54-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp313-cp313-macosx_11_0_arm64.whl (5.9 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

pkpython-0.0.54-cp313-cp313-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

pkpython-0.0.54-cp312-cp312-win_amd64.whl (5.8 MB view details)

Uploaded CPython 3.12Windows x86-64

pkpython-0.0.54-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp312-cp312-macosx_11_0_arm64.whl (5.9 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

pkpython-0.0.54-cp312-cp312-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

pkpython-0.0.54-cp311-cp311-win_amd64.whl (5.8 MB view details)

Uploaded CPython 3.11Windows x86-64

pkpython-0.0.54-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp311-cp311-macosx_11_0_arm64.whl (5.9 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

pkpython-0.0.54-cp311-cp311-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

pkpython-0.0.54-cp310-cp310-win_amd64.whl (5.8 MB view details)

Uploaded CPython 3.10Windows x86-64

pkpython-0.0.54-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

pkpython-0.0.54-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

pkpython-0.0.54-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

Details for the file pkpython-0.0.54.tar.gz.

File metadata

  • Download URL: pkpython-0.0.54.tar.gz
  • Upload date:
  • Size: 121.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.13.1

File hashes

Hashes for pkpython-0.0.54.tar.gz
Algorithm Hash digest
SHA256 bc9afc366f5c1dac1cd56f2d8b9b6d57fad03759cc9c6b1771ff656c86ae923e
MD5 8b1a7c4cecb0419abc60c5040ff4808f
BLAKE2b-256 d608eff83fa6a355163f78a393d08940c5089f1e5a09a15dfc214ad9dc5f2645

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6928979802bdcab0c5220a7d80a5ad2a3cb94fa19cfeb7196474c4a23737720c
MD5 5caec6b4b4938f9b0d3fb2c6736be892
BLAKE2b-256 54d99f8e5d9ed20eba482a880cc2b4ded0bc9ec0cb8311cc9264e59df075459a

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 78555080830f1e2e082cd983ff36f802ac69e65dfe5be626251d2fec067479cb
MD5 551f855632b766226e464fe0fb36f774
BLAKE2b-256 46ecd634380743b4278e98d57c23de9ef33fc03ec40ea53d88a0166b2cd72b3c

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 dcb9fd95f9bb7d53f55f21ad93f8c859b44f0501a253e063b95988466fb95daf
MD5 53831876160762e828c866c00b65c8eb
BLAKE2b-256 c7d932e3204639fa5fb038de32893c8c17d6b41059bba99c835ff91a6d436689

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314-win_amd64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 04a8bc6b3ca368cd30c35204fa41df4f281995aa000ce29e5c23619be26009b9
MD5 40f8c0ca2e8d211ed5d4e63d25a4f264
BLAKE2b-256 d0be7d561586196bf415633f6b196ce7cc36791a38866b0d3f0f151c033c81ce

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 85ebd1e5f30a5402a0c12fa530574df9ceb38367a9bc79b4656f46b5053318b7
MD5 f93d0f3f30de7dfe97bfbf3dcb72d8bb
BLAKE2b-256 23640facfd21288a0e4dd428ed09401d3f229a327eb322c4177867325241fbbd

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c45558ee8d79f89144adcb8f4284d41e822cc8d92d2c2b435239ca562fa2d758
MD5 bef8309bb3abd37bfca33938f650ac84
BLAKE2b-256 9ceb81f583b5fd3401bb2c97e7b747ec411125ed418061d852917a5311ba8f4e

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 35c8c034aea4971a41a91b0f5ce34324704ff5ea85e10b8ab05d61b3c1925376
MD5 7eae9b61c6b461008d56f2ab5b949dc3
BLAKE2b-256 b73182667ad9a921426bc3b407c3fdb857a46c707b5b221c62742baeb924f44d

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp314-cp314-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9a664964f26c8a9f1392eb422de57836483a520ab5865292951d5d6f9fc12448
MD5 f740b881415d64b767208ddc47e158a1
BLAKE2b-256 38ce2eab6d0e9ac2304a665956fa8860fc78419ed46b5fb4c084d9ce463e1bd5

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4e09417683596105cf18b7317017860a7d3c6d648f32d4d11b602ddfd7a6aa2a
MD5 e3a92e26b0914b55472ff0be1a17f0bf
BLAKE2b-256 82106154b193e3895dff47f6462b832b616ef6b5ffb73a306ef51e733238f334

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 23cd56833a0dc94b264f17ed5f0c150f438a2d047214ac4ff7a8f07779c6433f
MD5 ba5ab56aa75f844a26f6ee48bf48fdc4
BLAKE2b-256 f92bc53cde9d2c0aa1cbca24d762cd7167e6930c7b8ee5979491c7423c78953a

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 57fa759d9a66330b6b5350dc360990b1c132f767372b33976f062dcbd4eb45eb
MD5 89388733d74c094caa06e4f05295e2d3
BLAKE2b-256 78e9eef3fad8256adab9413b1a71628a3d2702fe72144f0a0ff4dece07f743ea

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1456e71493f03f992d26a3da6cefb4b1e12fd04ff144f6ccd88add1913f1bbd0
MD5 cd695bef8c82271820bcabf8c3a75718
BLAKE2b-256 0c9e556acf129e19c990155dc7385534c496d38135cd3c5db1efbe4f9b2d076f

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a76c4d37638d6d1a85fa96e0612573e87eff5dc20276ab7fffe0cb9714568dd7
MD5 883b89a104636cb3f369edb608e867ce
BLAKE2b-256 ef7fd197fd139f9521327196a8e014c46af787e62f9c660a9c78be5f1a0e61d5

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ac6320a9091156e779f37ae9d8d9566ab55bdc42da5454ffab9b8042efa71faa
MD5 e870eb885f65f30a05e88ee6fec7e9f2
BLAKE2b-256 af84838d649ae2244110d194f40a05c5fb41177504f84ae85dff2d2e25ee3d1b

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 98e8d7d3fdb8272a80473cf736e2794754cd80f0e3db94ddc3bbee1fb84a2d5c
MD5 02dae04c5753c86338a64247ae51b0ec
BLAKE2b-256 5edc8b7aa462d0ac1953fcc7c87f40b36e20bd2a36f6adf90d65d1744ae09793

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0f90a131faa4d318eab9be5f1653b1c9dc7ebcdf1d4488184ffe4f8a577b817f
MD5 3f01a0649ab6be70ee3d049be95fcf54
BLAKE2b-256 3faa4eb7cb573a0f3b2012ce4b854deac16bf390abaf623b56161be003b7e51c

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1c70e7dc00870c1e105ec0cb2b0cbfe9798cecda025b3b214a2e5a1edcc1fc81
MD5 cf5dcf4c988028b6f230cd246b2a5f75
BLAKE2b-256 68f148fbfd953b9fa198ae3c79d618842314804534ea7ef5c26010274d53d50c

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d4ff5250055073c70a3f03351a7d32d4c2873343048492d71678f560694c789e
MD5 439d3425b4e52c9621f74c5d0d3744a8
BLAKE2b-256 e4a9554a42df0c2b719522b0fe24210c60a85be7ec3ea2ca4ffc775cec1cc263

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 24ad99f379318b37991e7e3089b0ca03c624171e3e634407c1f0a7d1e00541dc
MD5 d2b51456d00716c8cfdda873f5a80260
BLAKE2b-256 85976c33d900b0771b8a34f8fa8241c8a60bdfd2506b97557b034c4ac6ecaa4e

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 ebb07f66b96b3fe893689468de255545dcc8b910269be02cf7c02654d2497d6a
MD5 f415fd9c79a18a110df1bd7590fce694
BLAKE2b-256 e2d6cce6f7f3ce31d5996179f34c1fad07e0b504f473ede8266e8cbb9abe83b5

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 21f101ec0d9afea42ed2bcbde8f971f01a3e640904d2236368bcac7df66dd155
MD5 74b0007632a79efad105f2cf6434fdbd
BLAKE2b-256 31dc1390978c3e1aafe6267f0f718c828b886b9540f95b370614841fb497dee3

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5bd4319827540a363c21066f03dba3b3a82243f5b5e91899b9e645bd2bb2250c
MD5 35e0ecd284acf73f6303575f57fd1189
BLAKE2b-256 7e8fc4ca251b78e979008564e3a0219531f9ce6e37d5b4ae494939e61a74f296

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 06a659434d56b5f0db6a03f2cf6ef65a2569f684fffccd34fc03ebd03ed09db9
MD5 0f043dc61c1dcd9f0a2b2c7a52c23bda
BLAKE2b-256 e3a0586975b9eaa89f80580313e60174c01e64570f431b4c022b676b98ff77cf

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 2d1042101c649ec2a75352391b1eb77836899587d30304ab91e14182e41e68a9
MD5 14a8bd542fa6def4dc8c41b65817272a
BLAKE2b-256 c3151d6e347c5b1fa330aa235df1aa0a8d3f9d8a67adea588af8fa3a8bcc8625

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 03f24e6947ada5c0ab7ad992656fd53f8c852ee2f5475f965b5f0d81ad6adb8a
MD5 728e06611463c62172ff5e2fafda60a1
BLAKE2b-256 f2b6eb9d4355a444928c85de8048e43d23f94a31b0b9039f797b11517d28446a

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4bc8e26b8a45ebfe76a2a7a14ef857bbb1ad65d5583d4da4536609f11eb15fba
MD5 2b2fa01ffd2335fa06c00c90ebee1412
BLAKE2b-256 53dbdc1a0c0da8972a6844a8c9d135e2e4b0df61235da7a4b48b4c4339444d51

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 50f10ab229882dc8d0e0472f0b4efb615481d1dab0bd7287cc635958953b64f9
MD5 fe5baa6aa35647d2716c9f187305e73e
BLAKE2b-256 5ecdf23e213ec898d0080b4f8463ad36aa4df003fd9b900b6895864ed31f7b29

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3bb2741b8533698f5ea8020a075e1d8519ccfbb0af82079f6fd340eff30b6d36
MD5 4a57a0c83a9f481815e3df9ffff7229b
BLAKE2b-256 5e74696cbf492f62ec626e0e794dc08cc8cdcb75c7802b496881529a7f1f377a

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ba40e2737f1a69fbceebbda44ed19fca0994f8f2e479ffcb79fafc3bc0904ee1
MD5 4c85486b3c32d1808afd08d271b58946
BLAKE2b-256 3d1cb31e42b6e3398767ff7c643b9abe336f65f19a468558e112383aca410964

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 79643b1f347121ce1e628a9b2a5f33a4669228b82a8dffb38f993848994726f9
MD5 2fa1d619bb3dc7a7649c04daa9a8252c
BLAKE2b-256 ea1ce9210ceccd206184ba4f1c0b5d6fa6ff49fe2246e07a139b2ef3dec0cf62

See more details on using hashes here.

File details

Details for the file pkpython-0.0.54-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pkpython-0.0.54-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 72999f79c75085e25213f9580398d3fc830ce06d83debd2dc07a7af3598a18a3
MD5 ecb9b416ea7a951fc6f6e21c85ad4324
BLAKE2b-256 26db6fe39fc884a010c03ad1298a22ab172fab03b5091783e37f08b81a795efe

See more details on using hashes here.

Supported by

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