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.2.1.tar.gz (121.5 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.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ x86-64

pkpython-0.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.15tmanylinux: glibc 2.17+ x86-64

pkpython-0.2.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.15manylinux: glibc 2.17+ x86-64

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

Uploaded CPython 3.14tmanylinux: glibc 2.17+ x86-64

pkpython-0.2.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp314-cp314-win_amd64.whl (5.9 MB view details)

Uploaded CPython 3.14Windows x86-64

pkpython-0.2.1-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.2.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp314-cp314-macosx_11_0_arm64.whl (6.0 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

pkpython-0.2.1-cp314-cp314-macosx_10_12_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.14macOS 10.12+ x86-64

pkpython-0.2.1-cp313-cp313-win_amd64.whl (5.9 MB view details)

Uploaded CPython 3.13Windows x86-64

pkpython-0.2.1-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.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp313-cp313-macosx_11_0_arm64.whl (6.0 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

pkpython-0.2.1-cp313-cp313-macosx_10_12_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

pkpython-0.2.1-cp312-cp312-win_amd64.whl (5.9 MB view details)

Uploaded CPython 3.12Windows x86-64

pkpython-0.2.1-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.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp312-cp312-macosx_11_0_arm64.whl (6.0 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

pkpython-0.2.1-cp312-cp312-macosx_10_12_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

pkpython-0.2.1-cp311-cp311-win_amd64.whl (5.9 MB view details)

Uploaded CPython 3.11Windows x86-64

pkpython-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

pkpython-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp311-cp311-macosx_11_0_arm64.whl (6.0 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

pkpython-0.2.1-cp311-cp311-macosx_10_12_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

pkpython-0.2.1-cp310-cp310-win_amd64.whl (5.9 MB view details)

Uploaded CPython 3.10Windows x86-64

pkpython-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

pkpython-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-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.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ ARM64

pkpython-0.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.0 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ ARM64

File details

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

File metadata

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

File hashes

Hashes for pkpython-0.2.1.tar.gz
Algorithm Hash digest
SHA256 179cf76e0d79cef6eacef45aa7c73d1ff840d674072fc6d72326548b573e4f96
MD5 bc45d2527cec695c3677cdfb45d0697d
BLAKE2b-256 120808eb479ca4c11b829b7d3d281b290d294fe5e101a8c06be552d8bb093c4d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 937db21dd078fc171dddd9ce054de723ca27efeaf63551408e933b2f8b35d511
MD5 534340a4e87bf0b9ce7f3cfa4871fa2f
BLAKE2b-256 54dbbf409c20181f98ab20029e258e47b5dd14093929378e72ee6bd9ff877f2a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6a9954eb7a55a1e4399a8e807c7243a64357c0afaf677a631254c5c22a4eba09
MD5 617414c65661eb9db7c38b282e552079
BLAKE2b-256 36532377a5f1d8b1decbe06f9f0646af6414eecdd0e36583b55bdf2ad10a7c1f

See more details on using hashes here.

File details

Details for the file pkpython-0.2.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.2.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c9e2a6ad26c0f0b506deddb5559c4f9ea507dc41964c67a04e7784d355c711b5
MD5 d0f508c644ec55ac1671a41fcae9ab5d
BLAKE2b-256 be60dfa08a4f70500dc722e094ad6055610f59b71cd9e0db5b6cba2378eb92a6

See more details on using hashes here.

File details

Details for the file pkpython-0.2.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.2.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 eb610277f7bf0b0af43845f60653c1b5ef4b00606719f709f7c29eca32f37258
MD5 21745c147cf1ac24a3400260ea4921d2
BLAKE2b-256 9a319ec0d800441e0001811e50d044c4a800aebe1260ddac5b17077d6ce87fb3

See more details on using hashes here.

File details

Details for the file pkpython-0.2.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 de0fb9ca178e1cdcab1a1cccf3877c099b9ab05941b2f5e7823f0a4158e3dd6f
MD5 4509eeaa6a89ea420fc072f94d9af736
BLAKE2b-256 3016afb169d01b7a36f16ccf97a18364d8bac0b3e4f403019d49a669bef3f297

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 962ad6388c980d5b7af7ac9b83dac873a8296a265549a812d5beb8a927154471
MD5 2ea7b7ddfa3c71c8f189e418a69f8240
BLAKE2b-256 56860df911d835c0ca2846b25d84901c923126c8b804fcad0832742ad2817dda

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 93a4b8f34cfede7ea181736df3e623bb288cfae6b01705d2a27185231f02e64a
MD5 3e2253b8fec78f800700e9a223dd1be0
BLAKE2b-256 c449d83ded86658dd3e6b9a44f83c841cab6a671e5c6190d04c2de275ec9f494

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 31c366fe441d1a0f643a264f0bee30116bce8c7457bbc77073f5351b0fec85fe
MD5 260f52aee0e0b22c0905a1ff61307da4
BLAKE2b-256 a364cba4a1f857eaa37df21318599bb1b01c71bb966530eb871ea7a063a5d083

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 d59814cac1a2526975e2bf85068903d66a517b0b8be030dca723e334667a119f
MD5 8d8dad4186c07debf03084f21c9d4173
BLAKE2b-256 e3ce5c94d3e9d510e7b48bd3a9a1d6e65b17f8de70e0b792beb33bcbe4d22919

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6856d56d9e5be060e1afa04c82274ba0ef4fa6d176092d3215a08688c686c2d4
MD5 2e8c3b303bcafeabca0578c93ca53192
BLAKE2b-256 a98432f0ca8f17f601c570c75ac651c0bd6d49be2463f54fdaa1cf397ed5bd51

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp314-cp314-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 10d737460881455bf6d93cb3e98bc3bc0e546fba38c12e5bb6c384c3dda1f547
MD5 1525246ffd59caace85e02b1b40cae4c
BLAKE2b-256 a9097e9f6779580d5d77a54e2660d54642f63b956340db3206fa1513c81c7d3d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 659004bbbb95307822b555f2453576ce855c1a8991fa4ab63c0d5e4bc32edd55
MD5 cd5adc6a946e74ba4ad1778cc8f2913a
BLAKE2b-256 6c402e858c18173bccc44bcc79648bdf77db3d5550076d4e41c4fec36b6f7c2a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 45a8ea52993c3b24523f5defb1c636a679d95319d6b451a19b2a6513f8f87e9b
MD5 b7cce26c6d123f74bbf5664ebea6ef1c
BLAKE2b-256 e2c6dc1ea002e5ac3edd67b5c0139205c8e232598798d77ea11b4f7fc7f48109

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1ffe9fb70104272f6fea2eeb1fdc302127fc3c40fcb35beb717e2af773859647
MD5 eeb1c6a4fa1de5589cae91422004819c
BLAKE2b-256 a242c47650d247b8fc59f16ba6a9c030b01f853e2c269ee20d5fd33dc257829c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5d6df082009ca09b7d6eac1003911103f8debd202b45d4b2ca7a542630357be7
MD5 ae5b15913c36c9372def51c57b3ce49a
BLAKE2b-256 318b77808cf80f64b543999e4e98abad2b2cf04eedc4640033130a76b3fd8d15

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c088bf50a0dd3e4c44ba6d1f4eb5ef25e87233ab6152977473b1489a360b5288
MD5 c8220496955adff35d75feb5d263d924
BLAKE2b-256 27c909af712fa8185a7dd77cc8cf615c94f7ec22566012f9f255f40d2468ab8f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 eb718fbe7c927fa07f1e03fe7a5c242b4a4b656c16a605a51c137da9f290ccf7
MD5 1be9ee73e6021de8256753cab45b15e9
BLAKE2b-256 03ee21272d3460d2b3b7076c74e46b97efafb2f2401a6cb786d78367d1559b8c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cc67cb3ebd7a2c7ad591a83fccb5652f9ced235c0c2fd3d514072ccf5cd24448
MD5 8a72d32a751bd680ed5a8e787a7212d0
BLAKE2b-256 cac43776c9457f63b03e860371d55d69c91e755666d10eaba45085430f2dba5e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ffd70bfa22a9bf1372adef3c4c5a5e3c7888199d38b52707d6fb23b4a691d42f
MD5 21018cbad5e284a41e4775a3b9ce81b7
BLAKE2b-256 f5adb63fb0befd959a152b9926ebbb34c603738a258b51cd430c846ee46c4154

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0a69d93188ce640f79418fae607cfc33188f6fb6ce63cb374d1be0e18016d883
MD5 d0ed12c11d5302154ff364d6c733afaf
BLAKE2b-256 52542ace61bdee4bb70ed0ea7b1e8fb43feb2e7819ee3d13ee044d1fc2b90029

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 638ee1ad1e3dd70ae8b11095782cc3e209aa8c149d3943d62323d0428c18593d
MD5 b19dba01a4217ddc007ad884fe59fd31
BLAKE2b-256 edcc9914af331fd2c77a78d7d66ed76286397ed48cafe17a1028a6e3b6948b32

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 451ec03ec38d5329e6dce269eb12883a6e82437418801bbe226466cf9829c6e0
MD5 69817ca9eb94a052d6bb4d04519cfdcd
BLAKE2b-256 a0ed8588f0c9b9f85d41cb9489c948182fa6d6366e8953a36bebf8fbb275cae5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bd3b8bb291d0317c95fe5afd10c4578754bef8e09034bb933404cfeb6c306c2f
MD5 853e332ad8b18494ccd92213f363d71d
BLAKE2b-256 e1a647496ef37828774f6ab386adaa1398eb314379d160581cfb6fa6e3ac458d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b29096464596cb275cd8c7127e2130e5ce30e1cab27e0c0b4581e9a405d278e9
MD5 ec542f5b1474af2f2b1b68f02e0e1ff2
BLAKE2b-256 55eb6ea556f6a57dfae2b110b6f40380e275a60fefd98fdafd71807be8f8797f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 56ff512db7e9a4a1779d1226772d6a3a1ffe465004659369845fa9f4f5f44089
MD5 69b7dba658e4feea973a5369f256a851
BLAKE2b-256 4e2e59fbef1536512f2cb79f587adf68dccb5cc368afadb911e3e76bd7997cd2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 42f3e3b23340a418b62295a89964dde865b522f3db70ec8593d65037e9be2a8b
MD5 8bd8a2aa83cb460b567885a10c920b34
BLAKE2b-256 e824ccb824b3101cfbc6a791dc671bde72f48269a9260b3dbe1e8bf6e23de84e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 488cccafceccd9d8476cf6086fae3abed177948b8c3651b4f12b70f2078dc4ff
MD5 0b833d7daaccf8f51c9f4776ce5e75ea
BLAKE2b-256 219a3af28a89d80564baf463a25a6142531186d179b86daa5cc52442ab2c6cc2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e767cb36f295bebb459836e237170b36cff55aaf23e3f2f90f90469351853f95
MD5 607548de9ea4ae98894f62189e1a89a4
BLAKE2b-256 eb826ae4cebbd1393db4973790477601763bf3b8a0fe3024da621c901a25cc2b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9452804c32f87efe30dc624d8feaeb0f27149d7f1baf348818acb9fc23254af4
MD5 9f77ffdc46e63196101789438689900c
BLAKE2b-256 723e06995a5845e7d45c4ecbf1b507c293fc858d7d044edeef90c16de913a1db

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4b19addc4efe16796c884afffb8b8a01a8f67217c2d5a6afece99c1770c85342
MD5 492c236d54096a3db4e864efda9a3dc8
BLAKE2b-256 300130d00894e87e51a7e07f52f35737ff7fe485ec922893d74d6c3b7bce1d93

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 ed5aa3bf4560194d07f6d154bcc42b6e329fa2442ee1bc0917099b228f4234d2
MD5 83880e2558c9949f9ba7d7155bbdb26c
BLAKE2b-256 47cab1f5939613cf17f1698f45f9b13b8fb32ef1757d512869f8f0d8bd4f2299

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pkpython-0.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1b3e0c171b9b88e11f2f1559ad18ab5a79af07c005aad55814e924a9e4b95c6b
MD5 452f3806c885876fe697b1fb93a53229
BLAKE2b-256 af465e21c4b4ee99062e168e1fd41bb70a9003e28ea90dcf306663ca5ac39aad

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