Skip to main content

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

Project description

CI 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.1.0.tar.gz (85.4 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.1.0-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.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

pkpython-0.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.14Windows x86-64

pkpython-0.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

pkpython-0.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.14macOS 11.0+ ARM64

pkpython-0.1.0-cp314-cp314-macosx_10_12_x86_64.whl (5.8 MB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

pkpython-0.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.13tmanylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.13Windows x86-64

pkpython-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

pkpython-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.13macOS 11.0+ ARM64

pkpython-0.1.0-cp313-cp313-macosx_10_12_x86_64.whl (5.8 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

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

Uploaded CPython 3.12Windows x86-64

pkpython-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

pkpython-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.12macOS 11.0+ ARM64

pkpython-0.1.0-cp312-cp312-macosx_10_12_x86_64.whl (5.8 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

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

Uploaded CPython 3.11Windows x86-64

pkpython-0.1.0-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.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

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

Uploaded CPython 3.11macOS 11.0+ ARM64

pkpython-0.1.0-cp311-cp311-macosx_10_12_x86_64.whl (5.8 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

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

Uploaded CPython 3.10Windows x86-64

pkpython-0.1.0-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.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

pkpython-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

pkpython-0.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

pkpython-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

pkpython-0.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.8 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

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

File metadata

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

File hashes

Hashes for pkpython-0.1.0.tar.gz
Algorithm Hash digest
SHA256 af550ac28c8f8a1ea9c47c37123b4c3afba3ebb9e060a72ed81c72d529312daf
MD5 00d2ad9aba5f63693d044b3ad9a3af9e
BLAKE2b-256 725500cce2d31844b57bc7a5b4cbb5474fde2329c62938277bf2c74a1fcb4458

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3c0a41a830b1f41f0a50e7a86b55edfdc9ef9c63b5cfdecf6449bd83a75617fd
MD5 5567b80f48c15fc10949feb3b6abefba
BLAKE2b-256 7130bf53adad7c8b79113462c8526f0dea48e0e5e558cf655d8893bdec96ffd4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 d3d281800146a4e99008c7ccf1891d8384130823df81036b9930fe73433a9b48
MD5 a8b086b7c0b5c889814c004ecd53032c
BLAKE2b-256 7ba2823e7b73ba72b13152a7846cd65687130bba55f4c99329d40789e44c7f2d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0f927f2d758b09a011a3d2db010a426d963a1dca8cbc07cf6da6720172d1724a
MD5 154fbcbc29d147c8d52a68cef6892772
BLAKE2b-256 7fcd924842fe19f19a9efa70d55ce6401e5cfa8cd20f49577f65127a8dcabc4b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 8e0bdb5803b5de0dc97364232dd79b5088eaa1983e6640154f692713b7860fdb
MD5 1f4c0adee78cd7390c89d57466899bf5
BLAKE2b-256 bd9d94f223adeac69d5ac247ff2c925ae9ca45d7743b83fb62adebf0a94b96f1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b3df382549247a1bce36ff0c4de73b971e0ab12753f8f517d611c4f7a23faee7
MD5 3937d756f33eff1f0a9ac42987f15b85
BLAKE2b-256 2b118f7951936fb6b8eb9f533cfe9f5fa00c69723567ab60602f230eda802a3b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5984766a09c77e8b6f85e2572f766e124a2066ceba5d2f34421da8e92da73573
MD5 3d01ab90551b4b37da3c3598f471f34d
BLAKE2b-256 4bef52a607f87a34ccbd3841da1fb0246a00820161d91a115dc7ed0efb1766c7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5f6fa9d7cda385b6d6580305a8b0deaa44b2a9634f957c24425eaa15a275e8cf
MD5 6caff357e7abdcf971bedc289bf2928f
BLAKE2b-256 8aa376db30ca1479d961e7c4e614fea3c581392eaec4327dfa6974913c8e98d4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 97280f48bcc2cd7646f13272757b51621bb9f0c9b83efd9ad229036046712e44
MD5 fde32da6657dbcd8830a0699791be709
BLAKE2b-256 b7a606a40ce7d3e308e9cec3d8846421e08121e87cdfed66855dad416b03941b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a1bd3cbf86a7964ee8c1321c211e1b7ffffc1b2ae8a95403b796aa73e8ce294d
MD5 97dfb845fd999fb0d6957a37ac03c485
BLAKE2b-256 c3f325cbce8e0ce1a0b7e19dc77bf8402a72ceb38462be328a95896018b42cbf

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 f599f452f7f740c0e82c251ed1c83cb8021b300de72553d21d5f4178a45837f6
MD5 e8974805277e96517dc8a5aa71c1a81f
BLAKE2b-256 3cff37225a4058effbda97f72afc9500e0a5e2725282eaf688d2d49c2552b0dd

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f11b31ec8f81cebf25e7266cdd255fe1683cba6d85689b48cd05e878ea379af9
MD5 d865c025a5e2e2926dd52a7babc6bf1d
BLAKE2b-256 9779d45c2d7d3e585b723d9f5274e2aa418c2fa16cd73b7009d3d53199e2c50c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ac216c3d06f9dc3049938b51e35f22b08df6b091b754e84e8b67dcb2a13ffd7b
MD5 4722909b82aa4cb8a0fc279049be16c6
BLAKE2b-256 cd22343bbc0721cf25401ca3270c850ef8a21cf50af1508650f9048f2820af81

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 95eeebc735c4f65183a16b0a2bcbe0415e07b7cb19d0cd35622ff913b6453c89
MD5 a308b91f6dbc3368cfd3295ae0a6e4dd
BLAKE2b-256 ea8c85973bba8787270fa25859d8d964f2241971b4f9710469f938a43e580651

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 16617ed6a610c745728fccf888947ddaff33aa5ecd0d7fb618831ccf5956729e
MD5 2d0b576966ed52242e5f1be5d3a66244
BLAKE2b-256 38729fa56121c550cf93a3410a4b0d676bde878e381b179c5ee2a25a110b5b62

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 83977da1ef47ea350aeedc1a5be13803317653f9ecd7358b0921284d4066e469
MD5 b8a020e044076457a31e891263942a23
BLAKE2b-256 5e56e631eb1065f2527eac23b21905b03de9ec2fbac2ce3844811a3086341075

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9cb1e4979bd0332ff85575c225c9c78ae778e3bb184dde023d9a08b32c3c2deb
MD5 4f303ef3c64d28279f5049ea34fd26a4
BLAKE2b-256 e9918382a39e2a80b366699854333cf31e84e2f2ca6389f6c6e2c99005923bdc

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 0c351bad4aa2e15d8723383306845aec1b9a8afb2d58512b68bf6ac5beb4e5ec
MD5 3e966a1a5e90ad9593bb4fd4ba74a7ce
BLAKE2b-256 67286206504278091f92c049490f0ef3266dc5cb9a901be54622d22b312b4367

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 21268b04ca6dca21c911b95fbab3c0a96b72177b899bf1d687bd3f2b0e3136d8
MD5 e2c10af497e38d1a671227f0a60b0076
BLAKE2b-256 0a88b966f55b608b981e485a08eb72f5899155795f397740e3a94541da09cd26

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 08758e0c897cc078e73a0848c78cc6e157e487d6ffaa51949711f7ac42ab5ad9
MD5 980fde4c1cb833d92c9e0435dbb782ff
BLAKE2b-256 d0e3c266719b724e8668e855426a85c31a0bce560a870c34786627d07becfa4f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 f7da7ce5641b6484d60fda80cc750bc5e135b044fd0c84753d54159b9217f287
MD5 d674e3af7c0b9aa2f3237cc1c995053c
BLAKE2b-256 e5c06164e7a9b7ca1035d4ae24108ac047d5f256e147fce1c8c187f71bd21c6b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 036a45c1519547519e8c7be4232c839e5e6fefd616b2b9d10e189609afdeeb45
MD5 0745bdde70eca62e8134dc4e65480713
BLAKE2b-256 2e61362574295aacbdaf513ce352b3470f501dff7178a6ee0410213ba1f62298

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 fa770d1b309db20abf3667632f68652b864ef93309c24ec28bf3ce66f11614fd
MD5 717e341051cd01187b76d7d47a6009d4
BLAKE2b-256 70e14059d4575b3f8e11297ab2ebcc3b0ea66a86120faa1aa379b1239adcd257

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 cad87672d308419efe46db80babe9e925e66693f4baa7c4a7604c75b330fc801
MD5 f76899b0f59fbec03a75953ad7e48d00
BLAKE2b-256 28d081815ec3168ad778867e6ae2197c4f229b9d84541076111929bbb3edff69

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 91ef64af7105d7e418b0ed57502e40b349515fe26f61a8475bef4220c6cc2667
MD5 c5c89a769fcf3a2a9236178dca8e7a40
BLAKE2b-256 b940eb1fec3dbd74ad65ae2d746e25d12f01dda2d04c71554efafba63fc77177

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 3302dd46ebba91d105a8c24fb6387a63c9bde578678f974b43fae5c9952aba6f
MD5 860315cc2cc0a8e96a26d28a8237d684
BLAKE2b-256 c6d348b52aa20e401d8d1b443978339166cd38efba072206df1d274e12301ee2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ff74446be161b19023aad598f74693d72cfe622487efb835e65ebc01f8b17a6a
MD5 4653f00d5d39e8fdc28d6ab84bda28b8
BLAKE2b-256 cc8610b9a0ed2d87ff6f740dee5511e10cb94613b95ff65313c5fbae07bcd6d2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5bf998c8a53317e231ed33be82e712849d26002c3d3faeb333cf2177e68d284c
MD5 86a91e93e1dbc9be511974eebe121965
BLAKE2b-256 fc10e2c55d412766420e5809def26a20d85d8bef8dfd96934fc6cc59197cdc11

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cdef41f43c59aebc50a2db791a181a844a7b7e8564598693b1f6ffd22ead1c20
MD5 157b69425b933fd8a3edfd4a562bf244
BLAKE2b-256 7e04f679708053b541afc6627b23758e10031a688cfe6244f7c3e19e97ffe5e8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2e1619db24ff81cc83f40d445d7ed67f24ab8d348f4a3165b134136f4243d6a4
MD5 754694e4103432bba5af69fbbfaeabd8
BLAKE2b-256 90d63d2484c4f290090e5554e463499b5be33546dc445bf692cdc8dc9c310292

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 03f777065a046d10e5d4049a0fd28e039fcad722db864216a9a209beae82eb26
MD5 f882122458adcd6b5d238e8fca1e9bd3
BLAKE2b-256 07aeaff850e87bebcc537e8f9497c969266572b8967dad43759c8c46feff52d4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ff6651f8d353b92b15ff345e110ee6c2ab9ad38e7915adf4744cd3b53cb87274
MD5 856d77bd6aaa8a8904868566c3aedb7a
BLAKE2b-256 ae1c8bf8161d4491be442e8aa45b77ff18bcec366d7116627fc744761820ca71

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