diff --git a/src/Fable.Python.fsproj b/src/Fable.Python.fsproj
index 34028ab..e7eb9a7 100644
--- a/src/Fable.Python.fsproj
+++ b/src/Fable.Python.fsproj
@@ -23,11 +23,14 @@
+
+
+
diff --git a/src/stdlib/Builtins.fs b/src/stdlib/Builtins.fs
index 89cffde..8260362 100644
--- a/src/stdlib/Builtins.fs
+++ b/src/stdlib/Builtins.fs
@@ -270,6 +270,26 @@ type IExports =
?opener: _Opener ->
TextIOWrapper
+ /// Return the value of the named attribute of object. name must be a string.
+ /// See https://docs.python.org/3/library/functions.html#getattr
+ abstract getattr: obj: obj * name: string -> 'T
+ /// Return the value of the named attribute of object. If the attribute does not exist,
+ /// default is returned.
+ /// See https://docs.python.org/3/library/functions.html#getattr
+ abstract getattr: obj: obj * name: string * ``default``: 'T -> 'T
+ /// Sets the named attribute on the given object to the specified value.
+ /// See https://docs.python.org/3/library/functions.html#setattr
+ abstract setattr: obj: obj * name: string * value: obj -> unit
+ /// Return True if the string is the name of one of the object's attributes.
+ /// See https://docs.python.org/3/library/functions.html#hasattr
+ abstract hasattr: obj: obj * name: string -> bool
+ /// Return True if the object argument is an instance of the classinfo argument.
+ /// See https://docs.python.org/3/library/functions.html#isinstance
+ abstract isinstance: obj: obj * classinfo: obj -> bool
+ /// Return the type of an object.
+ /// See https://docs.python.org/3/library/functions.html#type
+ abstract ``type``: obj -> obj
+
[]
let builtins: IExports = nativeOnly
@@ -331,3 +351,12 @@ let __name__: string = nativeOnly
/// Python print function. Takes a single argument, so can be used with e.g string interpolation.
let print obj = builtins.print obj
+
+/// Return the value of the named attribute of object with a default.
+let getattr obj name defaultValue = builtins.getattr (obj, name, defaultValue)
+
+/// Sets the named attribute on the given object to the specified value.
+let setattr obj name value = builtins.setattr (obj, name, value)
+
+/// Return True if the string is the name of one of the object's attributes.
+let hasattr obj name = builtins.hasattr (obj, name)
diff --git a/src/stdlib/Heapq.fs b/src/stdlib/Heapq.fs
new file mode 100644
index 0000000..1c8f0dc
--- /dev/null
+++ b/src/stdlib/Heapq.fs
@@ -0,0 +1,34 @@
+/// Type bindings for Python heapq module: https://docs.python.org/3/library/heapq.html
+module Fable.Python.Heapq
+
+open Fable.Core
+
+// fsharplint:disable MemberNames
+
+[]
+type IExports =
+ /// Push the value item onto the heap, maintaining the heap invariant
+ /// See https://docs.python.org/3/library/heapq.html#heapq.heappush
+ abstract heappush: heap: ResizeArray<'T> * item: 'T -> unit
+ /// Pop and return the smallest item from the heap, maintaining the heap invariant
+ /// See https://docs.python.org/3/library/heapq.html#heapq.heappop
+ abstract heappop: heap: ResizeArray<'T> -> 'T
+ /// Push item on the heap, then pop and return the smallest item from the heap
+ /// See https://docs.python.org/3/library/heapq.html#heapq.heappushpop
+ abstract heappushpop: heap: ResizeArray<'T> * item: 'T -> 'T
+ /// Transform list into a heap, in-place, in linear time
+ /// See https://docs.python.org/3/library/heapq.html#heapq.heapify
+ abstract heapify: x: ResizeArray<'T> -> unit
+ /// Pop and return the smallest item from the heap, and also push the new item
+ /// See https://docs.python.org/3/library/heapq.html#heapq.heapreplace
+ abstract heapreplace: heap: ResizeArray<'T> * item: 'T -> 'T
+ /// Return a list with the n largest elements from the dataset
+ /// See https://docs.python.org/3/library/heapq.html#heapq.nlargest
+ abstract nlargest: n: int * iterable: 'T seq -> ResizeArray<'T>
+ /// Return a list with the n smallest elements from the dataset
+ /// See https://docs.python.org/3/library/heapq.html#heapq.nsmallest
+ abstract nsmallest: n: int * iterable: 'T seq -> ResizeArray<'T>
+
+/// Heap queue algorithm
+[]
+let heapq: IExports = nativeOnly
diff --git a/src/stdlib/Queue.fs b/src/stdlib/Queue.fs
index 31ec38c..ca69760 100644
--- a/src/stdlib/Queue.fs
+++ b/src/stdlib/Queue.fs
@@ -7,15 +7,15 @@ open Fable.Core
[]
type Queue<'T>() =
- /// Return the approximate size of the queue. Note, qsize() > 0 doesn’t guarantee that a subsequent get() will not
+ /// Return the approximate size of the queue. Note, qsize() > 0 doesn't guarantee that a subsequent get() will not
/// block, nor will qsize() < maxsize guarantee that put() will not block.
member x.qsize() : int = nativeOnly
- /// Return True if the queue is empty, False otherwise. If empty() returns True it doesn’t guarantee that a
- /// subsequent call to put() will not block. Similarly, if empty() returns False it doesn’t guarantee that a
+ /// Return True if the queue is empty, False otherwise. If empty() returns True it doesn't guarantee that a
+ /// subsequent call to put() will not block. Similarly, if empty() returns False it doesn't guarantee that a
/// subsequent call to get() will not block.
member x.empty() : bool = nativeOnly
- /// Return True if the queue is full, False otherwise. If full() returns True it doesn’t guarantee that a subsequent
- /// call to get() will not block. Similarly, if full() returns False it doesn’t guarantee that a subsequent call to
+ /// Return True if the queue is full, False otherwise. If full() returns True it doesn't guarantee that a subsequent
+ /// call to get() will not block. Similarly, if full() returns False it doesn't guarantee that a subsequent call to
/// put() will not block.
member x.full() : bool = nativeOnly
/// Put item into the queue. If optional args block is true and timeout is None (the default), block if necessary
@@ -34,6 +34,10 @@ type Queue<'T>() =
/// operation goes into an uninterruptible wait on an underlying lock. This means that no exceptions can occur, and
/// in particular a SIGINT will not trigger a KeyboardInterrupt.
member x.get(?block: bool, ?timeout: float) : 'T = nativeOnly
+ /// Equivalent to get(False).
+ /// See https://docs.python.org/3/library/queue.html#queue.Queue.get_nowait
+ []
+ member x.get_nowait() : 'T = nativeOnly
/// Blocks until all items in the queue have been gotten and processed.
///
/// The count of unfinished tasks goes up whenever an item is added to the queue. The count goes down whenever a
@@ -57,24 +61,31 @@ type LifoQueue<'T>() =
[]
type SimpleQueue<'T>() =
- /// Return the approximate size of the queue. Note, qsize() > 0 doesn’t guarantee that a subsequent get() will not
+ /// Return the approximate size of the queue. Note, qsize() > 0 doesn't guarantee that a subsequent get() will not
/// block, nor will qsize() < maxsize guarantee that put() will not block.
member x.qsize() : int = nativeOnly
- /// Return True if the queue is empty, False otherwise. If empty() returns True it doesn’t guarantee that a
- /// subsequent call to put() will not block. Similarly, if empty() returns False it doesn’t guarantee that a
+ /// Return True if the queue is empty, False otherwise. If empty() returns True it doesn't guarantee that a
+ /// subsequent call to put() will not block. Similarly, if empty() returns False it doesn't guarantee that a
/// subsequent call to get() will not block.
member x.empty() : bool = nativeOnly
- /// Return True if the queue is full, False otherwise. If full() returns True it doesn’t guarantee that a subsequent
- /// call to get() will not block. Similarly, if full() returns False it doesn’t guarantee that a subsequent call to
+ /// Return True if the queue is full, False otherwise. If full() returns True it doesn't guarantee that a subsequent
+ /// call to get() will not block. Similarly, if full() returns False it doesn't guarantee that a subsequent call to
/// put() will not block.
member x.put(item: 'T, ?block: bool, ?timeout: float) : unit = nativeOnly
- /// Remove and return an item from the queue. If optional args block is true and timeout is None (the default),
- /// block if necessary until an item is available. If timeout is a positive number, it blocks at most timeout
- /// seconds and raises the Empty exception if no item was available within that time. Otherwise (block is false),
- /// return an item if one is immediately available, else raise the Empty exception (timeout is ignored in that
- /// case).
- ///
- /// Prior to 3.0 on POSIX systems, and for all versions on Windows, if block is true and timeout is None, this
- /// operation goes into an uninterruptible wait on an underlying lock. This means that no exceptions can occur, and
- /// in particular a SIGINT will not trigger a KeyboardInterrupt.
+ /// Remove and return an item from the queue.
member x.get(?block: bool, ?timeout: float) : 'T = nativeOnly
+ /// Equivalent to get(False).
+ []
+ member x.get_nowait() : 'T = nativeOnly
+
+/// Exception raised when non-blocking get() is called on an empty Queue
+/// See https://docs.python.org/3/library/queue.html#queue.Empty
+[]
+type Empty() =
+ inherit exn()
+
+/// Exception raised when non-blocking put() is called on a full Queue
+/// See https://docs.python.org/3/library/queue.html#queue.Full
+[]
+type Full() =
+ inherit exn()
diff --git a/src/stdlib/Threading.fs b/src/stdlib/Threading.fs
new file mode 100644
index 0000000..fee6681
--- /dev/null
+++ b/src/stdlib/Threading.fs
@@ -0,0 +1,80 @@
+/// Type bindings for Python threading module: https://docs.python.org/3/library/threading.html
+module Fable.Python.Threading
+
+open Fable.Core
+
+// fsharplint:disable MemberNames
+
+[]
+type IExports =
+ /// Return the 'thread identifier' of the current thread
+ /// See https://docs.python.org/3/library/threading.html#threading.get_ident
+ abstract get_ident: unit -> int
+ /// Return the main Thread object
+ /// See https://docs.python.org/3/library/threading.html#threading.main_thread
+ abstract main_thread: unit -> Thread
+ /// Return the current Thread object
+ /// See https://docs.python.org/3/library/threading.html#threading.current_thread
+ abstract current_thread: unit -> Thread
+ /// Return the number of Thread objects currently alive
+ /// See https://docs.python.org/3/library/threading.html#threading.active_count
+ abstract active_count: unit -> int
+ /// Return a list of all Thread objects currently active
+ /// See https://docs.python.org/3/library/threading.html#threading.enumerate
+ abstract enumerate: unit -> Thread list
+ /// Return a new thread-local data object
+ /// See https://docs.python.org/3/library/threading.html#threading.local
+ abstract local: unit -> obj
+
+/// A thread of execution
+/// See https://docs.python.org/3/library/threading.html#threading.Thread
+and [] Thread(?target: unit -> unit, ?name: string, ?daemon: bool) =
+ /// Start the thread's activity
+ member _.start() : unit = nativeOnly
+ /// Wait until the thread terminates
+ member _.join(?timeout: float) : unit = nativeOnly
+ /// A boolean value indicating whether this thread is a daemon thread
+ member _.daemon: bool = nativeOnly
+ /// The thread's name
+ member _.name: string = nativeOnly
+ /// The 'thread identifier' of this thread
+ member _.ident: int option = nativeOnly
+ /// Whether the thread is alive
+ member _.is_alive() : bool = nativeOnly
+
+/// A lock object (mutual exclusion)
+/// See https://docs.python.org/3/library/threading.html#threading.Lock
+[]
+type Lock() =
+ /// Acquire the lock
+ member _.acquire(?blocking: bool, ?timeout: float) : bool = nativeOnly
+ /// Release the lock
+ member _.release() : unit = nativeOnly
+ /// Return whether the lock is locked
+ member _.locked() : bool = nativeOnly
+
+/// A reentrant lock object
+/// See https://docs.python.org/3/library/threading.html#threading.RLock
+[]
+type RLock() =
+ /// Acquire the lock
+ member _.acquire(?blocking: bool, ?timeout: float) : bool = nativeOnly
+ /// Release the lock
+ member _.release() : unit = nativeOnly
+
+/// An event object for thread synchronization
+/// See https://docs.python.org/3/library/threading.html#threading.Event
+[]
+type Event() =
+ /// Set the internal flag to true
+ member _.set() : unit = nativeOnly
+ /// Reset the internal flag to false
+ member _.clear() : unit = nativeOnly
+ /// Return true if and only if the internal flag is true
+ member _.is_set() : bool = nativeOnly
+ /// Block until the internal flag is true or timeout
+ member _.wait(?timeout: float) : bool = nativeOnly
+
+/// Threading module access and utilities
+[]
+let threading: IExports = nativeOnly
diff --git a/src/stdlib/Traceback.fs b/src/stdlib/Traceback.fs
new file mode 100644
index 0000000..f4c4c7d
--- /dev/null
+++ b/src/stdlib/Traceback.fs
@@ -0,0 +1,25 @@
+/// Type bindings for Python traceback module: https://docs.python.org/3/library/traceback.html
+module Fable.Python.Traceback
+
+open Fable.Core
+
+// fsharplint:disable MemberNames
+
+[]
+type IExports =
+ /// Print exception information and stack trace entries
+ /// See https://docs.python.org/3/library/traceback.html#traceback.print_exc
+ abstract print_exc: unit -> unit
+ /// Format exception information and stack trace entries as a string
+ /// See https://docs.python.org/3/library/traceback.html#traceback.format_exc
+ abstract format_exc: unit -> string
+ /// Print stack trace entries
+ /// See https://docs.python.org/3/library/traceback.html#traceback.print_stack
+ abstract print_stack: unit -> unit
+ /// Format stack trace entries as a list of strings
+ /// See https://docs.python.org/3/library/traceback.html#traceback.format_stack
+ abstract format_stack: unit -> ResizeArray
+
+/// Extract, format and print exceptions and their tracebacks
+[]
+let traceback: IExports = nativeOnly
diff --git a/test/Fable.Python.Test.fsproj b/test/Fable.Python.Test.fsproj
index 539890b..188b590 100644
--- a/test/Fable.Python.Test.fsproj
+++ b/test/Fable.Python.Test.fsproj
@@ -15,6 +15,11 @@
+
+
+
+
+
diff --git a/test/TestBuiltinsAttr.fs b/test/TestBuiltinsAttr.fs
new file mode 100644
index 0000000..67b351b
--- /dev/null
+++ b/test/TestBuiltinsAttr.fs
@@ -0,0 +1,27 @@
+module Fable.Python.Tests.BuiltinsAttr
+
+open Fable.Python.Testing
+open Fable.Python.Builtins
+open Fable.Python.Threading
+
+[]
+let ``test hasattr works`` () =
+ let local = threading.local ()
+ setattr local "name" "test"
+ builtins.hasattr (local, "name") |> equal true
+
+[]
+let ``test getattr with default works`` () =
+ let local = threading.local ()
+ builtins.getattr (local, "missing", "default") |> equal "default"
+
+[]
+let ``test setattr and getattr work`` () =
+ let local = threading.local ()
+ builtins.setattr (local, "x", 42)
+ builtins.getattr (local, "x", 0) |> equal 42
+
+[]
+let ``test hasattr returns false for missing attribute`` () =
+ let local = threading.local ()
+ builtins.hasattr (local, "nonexistent") |> equal false
diff --git a/test/TestHeapq.fs b/test/TestHeapq.fs
new file mode 100644
index 0000000..6001186
--- /dev/null
+++ b/test/TestHeapq.fs
@@ -0,0 +1,39 @@
+module Fable.Python.Tests.Heapq
+
+open Fable.Python.Testing
+open Fable.Python.Heapq
+
+[]
+let ``test heappush and heappop work`` () =
+ let heap = ResizeArray()
+ heapq.heappush (heap, 3)
+ heapq.heappush (heap, 1)
+ heapq.heappush (heap, 2)
+ heapq.heappop heap |> equal 1
+ heapq.heappop heap |> equal 2
+ heapq.heappop heap |> equal 3
+
+[]
+let ``test heapify works`` () =
+ let heap = ResizeArray [5; 3; 1; 4; 2]
+ heapq.heapify heap
+ heapq.heappop heap |> equal 1
+
+[]
+let ``test heappushpop works`` () =
+ let heap = ResizeArray [2; 4; 6]
+ heapq.heapify heap
+ // Push 1, then pop smallest (1)
+ heapq.heappushpop (heap, 1) |> equal 1
+ // Push 3, then pop smallest (2)
+ heapq.heappushpop (heap, 3) |> equal 2
+
+[]
+let ``test nlargest works`` () =
+ let result = heapq.nlargest (3, [1; 5; 2; 8; 3; 7])
+ result |> equal (ResizeArray [8; 7; 5])
+
+[]
+let ``test nsmallest works`` () =
+ let result = heapq.nsmallest (3, [1; 5; 2; 8; 3; 7])
+ result |> equal (ResizeArray [1; 2; 3])
diff --git a/test/TestQueue.fs b/test/TestQueue.fs
new file mode 100644
index 0000000..13ddf31
--- /dev/null
+++ b/test/TestQueue.fs
@@ -0,0 +1,57 @@
+module Fable.Python.Tests.Queue
+
+open Fable.Python.Testing
+open Fable.Python.Queue
+
+[]
+let ``test Queue put and get work`` () =
+ let q = Queue()
+ q.put 42
+ q.get () |> equal 42
+
+[]
+let ``test Queue empty works`` () =
+ let q = Queue()
+ q.empty () |> equal true
+ q.put 1
+ q.empty () |> equal false
+
+[]
+let ``test Queue qsize works`` () =
+ let q = Queue()
+ q.qsize () |> equal 0
+ q.put 1
+ q.put 2
+ q.qsize () |> equal 2
+
+[]
+let ``test Queue get_nowait works`` () =
+ let q = Queue()
+ q.put "hello"
+ q.get_nowait () |> equal "hello"
+
+[]
+let ``test Queue get_nowait raises Empty`` () =
+ let q = Queue()
+ let mutable caught = false
+
+ try
+ q.get_nowait () |> ignore
+ with :? Empty ->
+ caught <- true
+
+ caught |> equal true
+
+[]
+let ``test SimpleQueue put and get work`` () =
+ let q = SimpleQueue()
+ q.put 10
+ q.put 20
+ q.get () |> equal 10
+ q.get () |> equal 20
+
+[]
+let ``test SimpleQueue get_nowait works`` () =
+ let q = SimpleQueue()
+ q.put 99
+ q.get_nowait () |> equal 99
diff --git a/test/TestThreading.fs b/test/TestThreading.fs
new file mode 100644
index 0000000..80d14ca
--- /dev/null
+++ b/test/TestThreading.fs
@@ -0,0 +1,20 @@
+module Fable.Python.Tests.Threading
+
+open Fable.Python.Testing
+open Fable.Python.Threading
+open Fable.Python.Builtins
+
+[]
+let ``test get_ident returns nonzero`` () =
+ let ident = threading.get_ident ()
+ (ident <> 0) |> equal true
+
+[]
+let ``test active_count is at least 1`` () =
+ (threading.active_count () >= 1) |> equal true
+
+[]
+let ``test local creates thread-local storage`` () =
+ let local = threading.local ()
+ setattr local "value" 42
+ getattr local "value" 0 |> equal 42
diff --git a/test/TestTraceback.fs b/test/TestTraceback.fs
new file mode 100644
index 0000000..8d08010
--- /dev/null
+++ b/test/TestTraceback.fs
@@ -0,0 +1,15 @@
+module Fable.Python.Tests.Traceback
+
+open Fable.Python.Testing
+open Fable.Python.Traceback
+
+[]
+let ``test format_exc returns string`` () =
+ // When no exception is active, format_exc returns "NoneType: None\n"
+ let result = traceback.format_exc ()
+ (result.Length > 0) |> equal true
+
+[]
+let ``test format_stack returns list`` () =
+ let result = traceback.format_stack ()
+ (result.Count > 0) |> equal true