├── .github └── workflows │ └── main.yml ├── .gitignore ├── INSTALLATION.md ├── ISSUES.md ├── README.md ├── pyffi-doc ├── info.rkt └── pyffi │ ├── info.rkt │ └── scribblings │ ├── examples.log │ └── manual-pyffi.scrbl ├── pyffi-lib ├── info.rkt └── pyffi │ ├── configure-pyffi.rkt │ ├── info.rkt │ ├── libpython.rkt │ ├── main.rkt │ ├── numpy-core.rkt │ ├── numpy.rkt │ ├── parameters.rkt │ ├── prefix-in.rkt │ ├── python-attributes.rkt │ ├── python-builtins.rkt │ ├── python-bytes.rkt │ ├── python-c-api.rkt │ ├── python-class.rkt │ ├── python-constants.rkt │ ├── python-define-delayed.rkt │ ├── python-delayed.rkt │ ├── python-dict.rkt │ ├── python-environment.rkt │ ├── python-evaluation.rkt │ ├── python-functions.rkt │ ├── python-generator.rkt │ ├── python-import.rkt │ ├── python-initialization.rkt │ ├── python-list.rkt │ ├── python-module.rkt │ ├── python-more-builtins.rkt │ ├── python-object.rkt │ ├── python-operators.rkt │ ├── python-slice.rkt │ ├── python-string.rkt │ ├── python-tuple.rkt │ ├── python-types.rkt │ ├── python.rkt │ └── structs.rkt ├── pyffi-tests ├── info.rkt └── tests │ ├── test-app.rkt │ ├── test-attributes.rkt │ ├── test-auto-numpy.rkt │ ├── test-builtins.rkt │ ├── test-dict.rkt │ ├── test-exceptions.rkt │ ├── test-exn.rkt │ ├── test-function.rkt │ ├── test-generator.rkt │ ├── test-has-module.rkt │ ├── test-list.rkt │ ├── test-module.rkt │ ├── test-numpy-game-of-life.rkt │ ├── test-numpy-misc.rkt │ ├── test-numpy.rkt │ ├── test-pyautogui.rkt │ ├── test-python-c-api.rkt │ ├── test-reference-counting.rkt │ ├── test-signature.rkt │ ├── test-slice.rkt │ ├── test-special.rkt │ ├── test-special2.rkt │ ├── test-string.rkt │ └── test-write-and-display.rkt ├── pyffi-tutorials ├── example-pygame-bouncing-ball.rkt ├── fish.csv ├── info.rkt ├── logo.png ├── pygame-intro-ball.gif ├── pygments │ ├── sxml-renderer.rkt │ └── test-scribble-with-pygments.rkt ├── test-numpy.rkt ├── test-pillow.rkt ├── tutorial-mix-racket-and-python.rkt ├── tutorial-numpy-fish-market.rkt ├── tutorial-pygments.rkt └── tutorial-skia.rkt ├── pyffi ├── info.rkt └── main.rkt └── python-scripts ├── signature-pyautogui.py ├── signature-random.py └── signature.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "main" branch 8 | push: 9 | branches: [ "main" ] 10 | pull_request: 11 | branches: [ "main" ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # Environment variables 17 | env: 18 | PYTHONPATH: "/opt/hostedtoolcache/Python/3.10.8/x64/" 19 | # PYTHONPATH: "/opt/hostedtoolcache/Python/3.10.7/x64/lib/" 20 | # PYTHONPATH: "/opt/hostedtoolcache/Python/3.10.7/x64/lib/python3.10" 21 | 22 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 23 | jobs: 24 | # This workflow contains a single job called "build" 25 | build: 26 | # The type of runner that the job will run on 27 | runs-on: ubuntu-latest 28 | 29 | # Steps represent a sequence of tasks that will be executed as part of the job 30 | steps: 31 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 32 | - uses: actions/checkout@v3 33 | 34 | # Install Python 3.10 35 | - name: Install Python 36 | uses: actions/setup-python@v4 37 | with: 38 | python-version: '3.10' 39 | # Python Diagnostics - Sysconfig 40 | - name: Python Diagnostics 41 | run: python3 -m sysconfig 42 | # Python Diagnostics - Sysconfig 43 | - name: Python Diagnostics - Is libpython to be found? 44 | run: python3 -c 'from distutils.sysconfig import get_config_var; print(get_config_var("LDLIBRARY"))' 45 | # Python Diagnostics - Names of modules in standard library 46 | - name: Python Diagnostics - Names of modules in standard library 47 | run: python3.10 -c "import sys ; print(sorted(sys.stdlib_module_names))" 48 | # Python Diagnostics - Names of builtin modules 49 | - name: Python Diagnostics - Names of builtin modules 50 | run: python3.10 -c "import sys ; print(sorted(sys.builtin_module_names))" 51 | # Python Diagnostics - Traceback 52 | - name: Python Diagnostics (traceback) 53 | run: | 54 | python3 -c "import traceback" 55 | echo "DATA" 56 | # /Users/runner/hostedtoolcache/Python/3.10.7/x64 # macos 57 | ls /opt/hostedtoolcache/Python/3.10.8/x64 # ubuntu 58 | echo "LIBS" 59 | ls /opt/hostedtoolcache/Python/3.10.8/x64/lib/python3.10 60 | # Python Diagnostics - PYTHONPATH 61 | - name: Python Diagnostics (PYTHONPATH) 62 | run: ls $PYTHONPATH 63 | 64 | # Install Racket 65 | - name: Install Racket 66 | uses: Bogdanp/setup-racket@v1.8.1 67 | with: 68 | architecture: 'x64' 69 | distribution: 'full' 70 | variant: 'CS' 71 | version: '8.4' 72 | dest: '"${HOME}/racketdist"' 73 | local_catalogs: $GITHUB_WORKSPACE 74 | sudo: never 75 | # Package Installation (pyffi-lib) 76 | - name: Package Installation - Local Packages 77 | run: raco pkg install --scope installation pyffi-lib 78 | # Configure Pyffi 79 | - name: Configure Pyffi 80 | run: | 81 | raco pyffi configure 82 | raco pyffi show 83 | raco pyffi diagnostics 84 | # Test Program 85 | # - name: Test Program 86 | # run: raco pyffi test 87 | # Package Installation (pyffi-doc) 88 | - name: Package Installation (pyffi-doc) 89 | run: raco pkg install --scope installation pyffi-doc 90 | # Setup Local Packages 91 | - name: Setup Collections 92 | run: raco setup -v --check-pkg-deps pyffi 93 | - name: Build documentation with raco scribble 94 | run: | 95 | cd /home/runner/work/pyffi/ 96 | pwd 97 | ls -F -G -R * 98 | xvfb-run -a raco scribble +m --redirect-main http://pkg-build.racket-lang.org/doc/ --htmls --dest ./htmls pyffi/pyffi-doc/pyffi/scribblings/manual-pyffi.scrbl 99 | - name: Push to Github Pages 100 | uses: JamesIves/github-pages-deploy-action@4.1.4 101 | with: 102 | folder: /home/runner/work/pyffi/htmls/manual-pyffi 103 | branch: gh-pages 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore files that end in ~ (Emacs autosave files) 2 | *~ 3 | \#* 4 | .#* 5 | .DS_Store 6 | *.bak 7 | TAGS -------------------------------------------------------------------------------- /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | PYFFI for Racket 2 | ================ 3 | 4 | 5 | 6 | ; /Library/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib 7 | 8 | 9 | python3 -m pip install numpy 10 | 11 | -------------------------------------------------------------------------------- /ISSUES.md: -------------------------------------------------------------------------------- 1 | Open 2 | ==== 3 | 4 | Issue 5 - Callbacks 5 | ------------------- 6 | Passing a Racket function to Python for use as a callback needs to be implemented. 7 | 8 | Issue 8 - Configuration 9 | ======================= 10 | - [done] Configuration tool via raco 11 | - [ ] Detect whether version >= 3.10 12 | - [ ] Test on Linux and Windows 13 | 14 | 15 | Issue 9 - Documentation 16 | ======================= 17 | - [x] Installation 18 | - [x] Introduction 19 | - [/] Reference 20 | - [ ] Tutorials 21 | - [ ] Numpy 22 | 23 | Issue 10 - Python on the Package Server 24 | ======================================= 25 | - [ ] is python 3.10 avaiable on the package server? 26 | - [ ] if not, make a python distribution? 27 | 28 | 29 | Issue 11 - Equal? [Nice to have] 30 | ======================= 31 | - [ ] make `obj` work with equal? 32 | 33 | Issue 12 - Match Expanders [Nice to have] 34 | ======================= 35 | - [ ] match expander for pystring, tuple, pylist and pydict 36 | 37 | Issue 13 - Hash and Dicts 38 | ========================= 39 | - [ ] implement hash->pydict 40 | - [ ] implement pydict->hash 41 | 42 | 43 | Issue 15 - Sequence [Nice to have] 44 | ================================== 45 | - [ ] Attach `prop:sequence` to pylist, pytuple, pydict and pystring. 46 | 47 | 48 | 49 | Nice to have 50 | ============ 51 | 52 | Issue 1 - Keywords in function calls 53 | ------------------------------------ 54 | - [done] Calling a function `foo` with a keyword not handled by the function 55 | results in an exception on the Python side. 56 | Check the keywords on the Racket side. 57 | 58 | This is partially resolved, since Racket now catches the Python exception. 59 | 60 | 61 | Resolved 62 | ======== 63 | 64 | Issue 2 - Exceptions on the Python side 65 | --------------------------------------- 66 | Handle exceptions raised on the Python side. 67 | 68 | Issue 3 - Reference Counting 69 | ---------------------------- 70 | Register a will for `obj` values that hold Python objects. 71 | The will executor must decrease the reference counter, so 72 | the Python side can reclaim the value. 73 | 74 | Issue 4 - Conversion between Racket and Python values 75 | ----------------------------------------------------- 76 | Change the automatic conversion of function arguments to: 77 | - [done] only convert non-compound result values 78 | - [done] handle reference counting for arguments converted from Racket values 79 | 80 | 81 | Issue 6 - Source location information lost for `run` 82 | ---------------------------------------------------- 83 | Rather than reporting "" a better source location is needed. 84 | 85 | src-loc: provoke0: Python exception occurred; 86 | ZeroDivisionError: division by zero 87 | File "", line 1, in provoke0 88 | 89 | Issue 7 - Dotted named and modules implemented via C Extensions 90 | --------------------------------------------------------------- 91 | For Python modules that support introspection via `inspect.signature` 92 | we can automatically create Racket functions with the correct number 93 | of positional and keyword arguments. Some Python modules implemented 94 | as C extensions also support this introspection, but some don't. 95 | 96 | In particular Numpy functions in general don't support this. 97 | 98 | In "numpy.rkt" and "numpy-core.rkt" we have Racket identifiers such as 99 | `numpy.shape` which clash with the automatic handling of dotted names. 100 | 101 | Idea: Have a list of names that prevent automatic handling of dotted named. 102 | 103 | Say after 104 | > (declare-special-prefix numpy) 105 | then 106 | > (numpy.shape A) 107 | will use `numpy.shape` and not `((getattr numpy "shape") A)`. 108 | 109 | Issue 14 - Lists and Tuples 110 | ========================= 111 | - [x] implement pylist->vector 112 | - [x] implement tuple->immutable-vector (use unsafe-vector*->immutable-vector!) 113 | 114 | 115 | Issue 16 - Generators 116 | ===================== 117 | - [x] Use `prop:sequence` for generators. 118 | 119 | 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyffi 2 | Use Python from Racket 3 | 4 | The rendered documentation is available here: 5 | 6 | https://soegaard.github.io/pyffi/ 7 | 8 | For examples with comments, see: 9 | 10 | https://github.com/soegaard/pyffi/tree/main/pyffi-tutorials 11 | 12 | Note: Only macOS and Linux are supported. 13 | 14 | If you want to add Windows support, mail me, and I'll give you some pointers. 15 | 16 | /Jens Axel Søgaard 17 | -------------------------------------------------------------------------------- /pyffi-doc/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define collection 'multi) 4 | 5 | (define deps '("scribble-lib" 6 | "pyffi-lib" 7 | "base")) 8 | 9 | (define build-deps '("sandbox-lib" 10 | "gui-doc" 11 | "pict-doc" 12 | "at-exp-lib" 13 | "base" 14 | "pict-lib" 15 | "scribble-lib" 16 | "racket-doc" 17 | "pyffi-lib" 18 | "rackunit-lib")) 19 | 20 | (define update-implies '("pyffi-lib")) 21 | 22 | (define pkg-desc "Use Python from Racket - Documentation of \"pyffi\"") 23 | 24 | (define pkg-authors '(soegaard)) 25 | -------------------------------------------------------------------------------- /pyffi-doc/pyffi/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define scribblings 4 | '(("scribblings/manual-pyffi.scrbl"))) 5 | 6 | -------------------------------------------------------------------------------- /pyffi-lib/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define collection 'multi) 4 | 5 | (define deps '("base" "at-exp-lib" 6 | ;; If we want to distribute Python as a package, we will need: 7 | ;; ("python-i386-macosx" #:platform "i386-macosx") 8 | ;; ("python-x86_64-macosx" #:platform "x86_64-macosx") 9 | ;; ("python-ppc-macosx" #:platform "ppc-macosx") 10 | ;; ("python-aarch64-macosx" #:platform "aarch64-macosx") 11 | ;; ("python-win32-i386" #:platform "win32\\i386") 12 | ;; ("python-win32-x86_64" #:platform "win32\\x86_64") 13 | ;; ("python-win32-arm64" #:platform "win32\\arm64") 14 | ;; ("python-x86_64-linux-natipkg" #:platform "x86_64-linux-natipkg") 15 | )) 16 | 17 | (define build-deps (list "base" "at-exp-lib")) 18 | 19 | 20 | (define pkg-desc "Use Python from Racket - Implementation part without documentation") 21 | 22 | (define pkg-authors '(soegaard)) 23 | 24 | (define version "1.0") 25 | 26 | (define test-responsibles '((all jensaxel@soegaard.net))) 27 | 28 | 29 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/configure-pyffi.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket 2 | ;;; 3 | ;;; Run this file to configure `pyffi`. 4 | ;;; 5 | 6 | ;; The Python module `sysconfig` provides access to Python's configuration information. 7 | ;; If we can load the shared library `libpython3` then we can use `sysconfig` to 8 | ;; get all the information needed. 9 | 10 | ;; However, we need a way to figure out where `libpython3` is, before 11 | ;; we can load it. 12 | 13 | ;; The most reliable way is to run `python3` in a terminal and let 14 | ;; Python tell us where the shared library is. 15 | ;; In order to make it easy for users, this file will find `python3`, 16 | ;; run it, and extract the needed configuration information. 17 | 18 | ;; We are using `(find-executable-path "python3")` to find the path. 19 | ;; In the terminal where the environment variable PATH is set, this 20 | ;; works fine. However, if run in DrRacket (at least on macOS) the 21 | ;; environment variable PATH won't be set and we risk picking up a 22 | ;; different version. 23 | 24 | ;; As I am writing this, DrRacket will find the system installed 25 | ;; version of Python in /usr/bin/python3 which is version 3.8.9. 26 | ;; In the terminal we find version 3.10: 27 | ;; /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 28 | 29 | ;; That is: 30 | 31 | ;; Run this configuration tool in an environment (terminal) 32 | ;; where `python3` starts the Python command want to use. 33 | 34 | ;; After determining the location of the shared library 35 | ;; the location is written to the file "pyffi/path-to-libpython3.conf" 36 | ;; in the users preferences with `put-preferences`. 37 | 38 | 39 | ;; Notes: 40 | ;; We run the command `python3 -m sysconfig` which writes 41 | ;; the configuration information in groups of key/value pairs. 42 | ;; The parser below parses the entire output (maybe we need 43 | ;; more information at a later date). 44 | 45 | (require raco/command-name) 46 | 47 | (define system-configuration #f) 48 | (define system-configuration-string #f) 49 | 50 | 51 | (define (get-configuration [given-path-to-python #f]) 52 | (define path-to-python 53 | (or given-path-to-python 54 | (or (find-executable-path "python3") 55 | (find-executable-path "python") 56 | (find-executable-path "python3.10")))) 57 | 58 | (displayln "Configuration tool for `pyffi`.") 59 | (displayln "-------------------------------") 60 | (displayln "This tool attempts to find the shared library `libpython3` ") 61 | (displayln "with the help of the `python3` executable.") 62 | (newline) 63 | (cond 64 | [path-to-python 65 | (displayln "The executable") 66 | (display " ") 67 | (displayln path-to-python) 68 | (displayln "will be used to find the location of the shared library.")] 69 | [else 70 | (displayln "The executable `python3` was not found in the current path.") 71 | (displayln "Double check that `python3` starts Python your terminal.") 72 | (displayln "Then run this configuration tool in the same terminal.") 73 | (exit 1)]) 74 | (newline) 75 | 76 | (define success 'not-available-yet) 77 | (define str-path (if (path? path-to-python) 78 | (path->string path-to-python) 79 | path-to-python)) 80 | (define command (string-append str-path " -m sysconfig")) 81 | (set! system-configuration-string 82 | (with-output-to-string 83 | (λ() (set! success (system command))))) 84 | (unless success 85 | (displayln "An error occurred while running the command:") 86 | (display " ") 87 | (displayln command) 88 | (displayln "Configuration of `pyffi` failed.") 89 | (exit 2)) 90 | 91 | ; (displayln system-configuration-string) 92 | 93 | (define (string->lines x) 94 | (string-split x #rx"[\r\n]+")) 95 | (define (blank? x) 96 | (equal? (string-trim x) "")) 97 | (define (trim x) 98 | (string-trim (string-trim x) "\"")) 99 | 100 | (define (string->key/value x) 101 | (and (string-contains? x ":") 102 | (match (regexp-match "([^:]*):(.*)" x) 103 | [(list full before after) 104 | (list (trim before) (trim after))]))) 105 | 106 | ; parse the output of `python3 -m sysconfig` into an association list 107 | (define (parse info) 108 | (define lines (string->lines info)) 109 | (let loop ([lines lines] [groups '()]) 110 | (match lines 111 | ['() 112 | (reverse groups)] 113 | [(list* line lines) 114 | (if (blank? line) 115 | (loop (rest lines groups)) 116 | (match (string->key/value line) 117 | [(list key value) 118 | (if (blank? value) ; new group? 119 | (let () 120 | (define-values (group rest-lines) (parse-group line lines)) 121 | (loop rest-lines (cons group groups))) 122 | (loop lines (cons (list key value) groups)))]))]))) 123 | 124 | ; each group is parsed into a sub-association list 125 | (define (parse-group first-line lines) 126 | (define group-lines (takef lines (λ (x) (regexp-match "([^=]*)=(.*)" x)))) 127 | (define rest-lines (drop lines (length group-lines))) 128 | 129 | (define group 130 | (list (string-trim (trim first-line) ":") 131 | (for/list ([line group-lines]) 132 | (match (regexp-match "([^=]*)=(.*)" line) 133 | [(list full before after) 134 | (list (string-trim before) 135 | (trim after))])))) 136 | (values group rest-lines)) 137 | 138 | (set! system-configuration (parse system-configuration-string))) 139 | 140 | ;; Now the information is available in `system-configuration`. 141 | 142 | 143 | ;;; PATHS 144 | (define (python-paths) 145 | (assoc "Paths" system-configuration)) 146 | 147 | (define (python-data) 148 | (define result (assoc "data" (second (python-paths)))) 149 | (and result (second result))) 150 | 151 | ;;; VARIABLES 152 | 153 | (define (python-variables) 154 | (assoc "Variables" system-configuration)) 155 | 156 | (define (python-libdir) 157 | (define result (assoc "LIBDIR" (second (python-variables)))) 158 | (and result (second result))) 159 | 160 | 161 | (define (python-bindir) 162 | (define result (assoc "BINDIR" (second (python-variables)))) 163 | (and result (second result))) 164 | 165 | (define (get-old-libdir) 166 | (get-preference 'pyffi:libdir (λ () #f))) 167 | 168 | (define (set-new-libdir new-libdir-path) 169 | (define old (get-preference 'pyffi:libdir (λ () #f))) 170 | (unless (equal? old new-libdir-path) 171 | (put-preferences (list 'pyffi:libdir) 172 | (list new-libdir-path))) 173 | (when old 174 | (displayln "The previous value of LIBDIR was:") 175 | (display " ") 176 | (displayln old)) 177 | (displayln "The preference for LIBDIR is now set to:") 178 | (display " ") 179 | (displayln new-libdir-path)) 180 | 181 | 182 | (define (get-old-data) 183 | (get-preference 'pyffi:data (λ () #f))) 184 | 185 | (define (set-new-data new-data) 186 | (define old (get-preference 'pyffi:data (λ () #f))) 187 | (unless (equal? old new-data) 188 | (put-preferences (list 'pyffi:data) 189 | (list new-data))) 190 | (when old 191 | (displayln "The previous value of DATA was:") 192 | (display " ") 193 | (displayln old)) 194 | (displayln "The preference for DATA is now set to:") 195 | (display " ") 196 | (displayln new-data)) 197 | 198 | 199 | (define (handle-libdir path-to-python) 200 | (cond 201 | [(python-libdir) => set-new-libdir] 202 | [(and (equal? (system-type 'os) 'windows) 203 | (python-bindir)) => set-new-libdir] 204 | [else 205 | (parameterize ([current-output-port (current-error-port)]) 206 | (displayln "The LIBDIR key wasn't found.") 207 | (newline) 208 | (displayln "The sysconfiguration produced by the Python module `sysconfig` was:") 209 | (newline) 210 | (displayln system-configuration-string))])) 211 | 212 | (define (handle-data path-to-python) 213 | (cond 214 | [(python-data) => set-new-data] 215 | [else 216 | (parameterize ([current-output-port (current-error-port)]) 217 | (displayln "The DATA key wasn't found.") 218 | (newline) 219 | (displayln "The sysconfiguration produced by the Python module `sysconfig` was:") 220 | (newline) 221 | (displayln system-configuration-string))])) 222 | 223 | (define (configure [path-to-python #f]) 224 | (get-configuration path-to-python) 225 | (handle-libdir path-to-python) 226 | (newline) 227 | (handle-data path-to-python)) 228 | 229 | (define (show) 230 | (displayln "Current configuration for 'pyffi'.") 231 | (newline) 232 | (display " libdir = ") 233 | (write (get-preference 'pyffi:libdir)) 234 | (newline) 235 | 236 | (display " data = ") 237 | (write (get-preference 'pyffi:data)) 238 | (newline) 239 | 240 | (newline) 241 | (displayln "Meaning:") 242 | (newline) 243 | (displayln " libdir: location of the shared library 'libpython'") 244 | (displayln " data: location of bin/ lib/ share/ etc.")) 245 | 246 | (define usage 247 | @~a{ 248 | Usage: raco pyffi 249 | 250 | raco pyffi configure 251 | configure 'pyffi' using auto-detected python executable 252 | 253 | raco pyffi configure 254 | configure 'pyffi' using 255 | 256 | raco pyffi show 257 | show the current 'pyffi' configuration 258 | 259 | raco pyffi diagnostics 260 | show the Python paths and variables}) 261 | 262 | (define (display-usage) 263 | (displayln usage)) 264 | 265 | (define (diagnostics) 266 | (define path-to-python #f) 267 | (get-configuration path-to-python) 268 | 269 | (displayln "Python Paths") 270 | (displayln "------------") 271 | (pretty-print (python-paths)) 272 | 273 | (displayln "Python Variables") 274 | (displayln "----------------") 275 | (pretty-print (python-variables))) 276 | 277 | 278 | (define (run) 279 | (command-line 280 | #:program (short-program+command-name) 281 | #:usage-help 282 | " 283 | raco pyffi configure 284 | configure 'pyffi' using auto-detected python executable 285 | 286 | raco pyffi configure 287 | configure 'pyffi' using 288 | 289 | raco pyffi show 290 | show the current 'pyffi' configuration" 291 | #:args args 292 | (match args 293 | [(list "configure") (configure)] 294 | [(list "configure" path-to-python) (configure path-to-python)] 295 | [(list "show") (show)] 296 | [(list "diagnostics") (diagnostics)] 297 | [else (display-usage) 298 | (exit 3)]))) 299 | 300 | (run) 301 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define deps (list "base" "at-exp-lib")) 4 | (define build-deps (list "base" "at-exp-lib")) 5 | 6 | (define raco-commands 7 | (list (list "pyffi" 'pyffi/configure-pyffi "configure pyffi" #f))) 8 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/libpython.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket/base 2 | 3 | ;;; This module loads the shared library `libpython` and 4 | ;;; provides the form `define-python` which is used to 5 | ;;; create bindings for Python's C-API. 6 | 7 | (provide define-python) 8 | 9 | ;;; Imports 10 | 11 | (require ffi/unsafe ffi/unsafe/define racket/file) 12 | 13 | 14 | ;;; Configuration 15 | 16 | (define libpython-folder (get-preference 'pyffi:libdir (λ () #f))) 17 | #;(unless libpython-folder 18 | (parameterize ([current-output-port (current-error-port)]) 19 | (displayln "There is no preference for 'pyffi:libdir' set.") 20 | (displayln "In order for `pyffi` to find the shared library `libpython3` (or `libpython3.10`) ") 21 | (displayln "you must set the 'pyffi:libdir' preference to the folder of the shared library.") 22 | (displayln "The most convenient way to do this, is to run `raco pyffi configure`.") 23 | (displayln "See details in the documentation.") 24 | (exit 1))) 25 | 26 | 27 | (define extension 28 | (case (system-type 'os) 29 | [(macosx) "dylib"] 30 | [(unix) "so"] 31 | [(windows) "dll"] 32 | [else (error 'internal-error:extension "File a bug report on Github.")])) 33 | 34 | (define (build-full-path name) 35 | (if libpython-folder 36 | (build-path libpython-folder 37 | (string->path (string-append name "." extension))) 38 | (string->path (string-append name "." extension)))) 39 | 40 | (define libpython-path 41 | (or (for/first ([name '("libpython3.10" "libpython310" "libpython3")] 42 | #:when (file-exists? (build-full-path name))) 43 | (build-full-path name)) 44 | ;; Github Action (Ubuntu) 45 | "libpython3.10")) 46 | 47 | 48 | 49 | ; (displayln (list 'libpython-path libpython-path)) 50 | 51 | ; Note: If the Python interpreter loads a shared library dynamically, 52 | ; it needs access to the Python C-API. To make the symbols 53 | ; exported by a shared library visible to other shared libaries, 54 | ; we need to use a "flat namespace" and therefore use `#:global? #t`, 55 | ; when loading the library. 56 | 57 | (define-ffi-definer define-python (ffi-lib libpython-path #:global? #t)) 58 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require "python.rkt") 4 | (provide (all-from-out "python.rkt")) 5 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/numpy-core.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (all-defined-out)) 3 | 4 | (require "python-types.rkt" 5 | "python-c-api.rkt" 6 | "python-environment.rkt" 7 | "python-evaluation.rkt" 8 | "python-constants.rkt" 9 | "python-builtins.rkt" 10 | "python-operators.rkt" 11 | "structs.rkt" 12 | racket/format) 13 | 14 | (require (only-in ffi/unsafe ->)) 15 | (require (for-syntax (only-in ffi/unsafe ->) 16 | racket/base racket/syntax syntax/parse)) 17 | 18 | ;;; 19 | ;;; Numpy 20 | ;;; 21 | 22 | ; Use (initialize-numpy) before using functions exported here. 23 | 24 | ;;; 25 | ;;; Initialization 26 | ;;; 27 | 28 | ; We need to: 29 | ; 1. Import numpy into the main module 30 | ; 2. Extract values such as dtypes from Numpy. 31 | 32 | (provide initialize-numpy) 33 | (define (initialize-numpy) 34 | (import-numpy) 35 | (initialize-dtypes) 36 | (void (run* "import operator as operator"))) 37 | 38 | 39 | (define (numpy-scalar->number x) (tell x item -> ~int)) 40 | 41 | ; Use as return type only: 42 | (define ~numpy-scalar (pytype "numpyscalar" (λ (x) (error 'todo)) numpy-scalar->number)) 43 | 44 | (define ~ndarray (pytype "ndarray" ndarray->py-ndarray py-ndarray->ndarray)) 45 | ; a shape is an integer or a tuple of integers 46 | (define ~shape (pytype "shape" 47 | (λ (x) 48 | (cond 49 | [(integer? x) (integer->py-int x)] 50 | [(vector? x) (vector->py-tuple x)] 51 | [else (error '~shape (~a "expected an integer or a vector of integers, got: " x))])) 52 | (λ (x) 53 | (case (python-type x) 54 | [("int") (py-int->number x)] 55 | [("tuple") (py-tuple->vector x)] 56 | [else (error '~shape (~a "expected an integer or a tuple of integers, got: " x))])))) 57 | 58 | 59 | (define ~dtype (pytype "dtype" obj-the-obj #f)) 60 | (define ~order (pytype "order" string->py-string py-string->string)) 61 | 62 | (define-syntax (define-dtypes stx) 63 | (syntax-parse stx 64 | [(_define-dtypes init-name name:id ...) 65 | (define names (for/list ([name (syntax->list #'(name ...))]) 66 | (format-id name "numpy.~a" name))) 67 | (with-syntax ([(numpy.name ...) names]) 68 | (syntax/loc stx 69 | (begin 70 | (provide name) ... 71 | (define name #f) ... 72 | (define (init-name) 73 | (set! name (obj (~a"dtype<" 'name ">") (get 'numpy.name))) ...))))])) 74 | 75 | (define-dtypes initialize-dtypes 76 | ; Note: float96 and complex192 not availabe on my system 77 | bool8 78 | int8 int16 int32 int64 79 | uint8 uint16 uint32 uint64 80 | intp uintp 81 | float16 float32 float64 float128 82 | complex64 complex128 complex256 83 | double longdouble 84 | csingle cdouble clongdouble) 85 | 86 | ;;; 87 | ;;; Attributes of `ndarray` 88 | ;;; 89 | 90 | (define (.T A) (getattr A "T")) 91 | (define (.data A) (getattr A "data")) 92 | (define (.dtype A) (getattr A "dtype")) 93 | (define (.flags A) (getattr A "flags")) 94 | (define (.flat A) (getattr A "flat")) 95 | (define (.imag A) (getattr A "imag")) 96 | (define (.real A) (getattr A "real")) 97 | (define (.size A) (getattr A "size")) 98 | (define (.itemsize A) (getattr A "itemsize")) 99 | (define (.nbytes A) (getattr A "nbytes")) 100 | (define (.ndim A) (getattr A "ndim")) 101 | (define (.shape A) (getattr A "shape")) 102 | (define (.strides A) (getattr A "strides")) 103 | (define (.ctypes A) (getattr A "ctypes")) 104 | (define (.base A) (getattr A "base")) 105 | 106 | ;;; 107 | ;;; Constructors 108 | ;;; 109 | 110 | ; numpy.array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, like=None) 111 | (define-py numpy.array (~fun ~list #:dtype ~dtype -> ~ndarray)) ; todo 112 | 113 | ; low-level constructor - the docs recommend using `array`, `zeros` or `empty` 114 | (define-py numpy.ndarray (~fun #:shape ~tuple ; shape 115 | #:dtype ~dtype ; default is float 116 | #:buffer ~obj ; object with buffer interface 117 | #:offset ~int ; offset of array data in buffer 118 | #:strides ~tuple ; tuple of ints 119 | #:order ~char ; "C" or "F" 120 | -> ~ndarray)) 121 | 122 | ;; ;;; 123 | ;; ;;; Methods of `ndarray` 124 | ;; ;;; 125 | 126 | #;(define-py numpy.all (~fun ~py 127 | #:axis ~py ; None or int or tuple of ints, optional, default None 128 | #:out ~ndarray 129 | #:keepdims ~bool 130 | #:where ~py ; array-like 131 | -> ~py)) 132 | #;(define-py numpy.any (~fun ~py 133 | #:axis ~py ; None or int or tuple of ints, optional, default None 134 | #:out ~ndarray 135 | #:keepdims ~bool 136 | #:where ~py ; array-like 137 | -> ~py)) 138 | ; numpy.arange([start, ]stop, [step, ]dtype=None, *, like=None) 139 | (define-py numpy.arange (~fun ~py ~py ~py #:dtype ~dtype #:like ~py -> ~ndarray 140 | #:first-optional 1)) 141 | ; ndarray.argmax(a, axis=None, out=None, *, keepdims=False) 142 | #;(define-py numpy.argmax (~fun ~py 143 | #:axis ~py 144 | #:out ~ndarray 145 | #:keepdims ~bool 146 | -> ~py)) 147 | #;(define-py numpy.argmin (~fun ~py 148 | #:axis ~py 149 | #:out ~ndarray 150 | #:keepdims ~bool 151 | -> ~py)) 152 | ; ndarray.argpartition(a, kth, axis=- 1, kind='introselect', order=None) 153 | #;(define-py numpy.argpartition (~fun ~py ; a 154 | ~py ; kth 155 | #:axis ~py 156 | #:kind ~ndarray 157 | #:order ~bool 158 | -> ~py)) 159 | ; ndarray.argsort(a, axis=- 1, kind=None, order=None) 160 | #;(define-py numpy.argsort (~fun ~py ; a 161 | #:axis ~py 162 | #:kind ~ndarray 163 | #:order ~bool 164 | -> ~py)) 165 | ; ndarray.astype(a, dtype, order='K', casting='unsafe', subok=True, copy=True) 166 | (define-py numpy.ndarray.astype (~fun ~py ; a 167 | ~dtype 168 | #:order ~char ; default "K" 169 | #:casting ~string ; default "unsafe" 170 | #:subok ~bool ; default #t 171 | #:copy ~bool ; default #t 172 | -> ~ndarray)) 173 | ; ndarray.byteswap(a, inplace=False) 174 | (define-py numpy.ndarray.byteswap (~fun ~py ; a 175 | #:inplace ~bool ; default #f 176 | -> ~py)) 177 | ; ndarray.choose(a, choices, out=None, mode='raise') 178 | #;(define-py numpy.choose (~fun ~py ; a 179 | ~py ; 180 | #:out ~py 181 | #:mode ~py 182 | -> ~py)) 183 | ; ndarray.clip(a, min=None, max=None, out=None, **kwargs) 184 | #;(define-py numpy.clip (~fun ~py ~py ~py 185 | #:out ~py 186 | ; todo: **kwargs ? 187 | -> ~py)) 188 | 189 | ;; >>> inspect.signature(numpy.clip) 190 | ;; 191 | ;; >>> inspect.getfullargspec(numpy.clip) 192 | ;; FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 193 | 194 | 195 | ; ndarray.compress(a, condition, axis=None, out=None) 196 | #;(define-py numpy.compress (~fun ~py ; a 197 | ~py ; 198 | #:axis ~py 199 | #:out ~py 200 | -> ~py)) 201 | ; numpy.diag(a, v, k=0)[source] 202 | #;(define-py numpy.diag (~fun ~py 203 | #:axis ~py 204 | #:out ~py 205 | -> ~py)) 206 | ; numpy.full(shape, fill_value, dtype=None, order='C', *, like=None) 207 | #;(define-py numpy.full (~fun ~py ~py 208 | #:dtype ~py 209 | #:order ~py 210 | #:like ~py 211 | -> ~py)) 212 | ; numpy.interp(x, xp, fp, left=None, right=None, period=None) 213 | #;(define-py numpy.interp (~fun ~py ~py ~py 214 | #:left ~py 215 | #:right ~py 216 | #:period ~py 217 | -> ~py)) 218 | ; numpy.isnan(x, /, out=None, *, where=True, casting='same_kind', order='K', 219 | ; dtype=None, subok=True[, signature, extobj]) = 220 | #;(define-py numpy.isnan (~fun ~py 221 | #:out ~py 222 | #:where ~py 223 | #:casting ~py 224 | #:order ~py 225 | #:dtype ~py 226 | #:subok ~py 227 | -> ~py)) 228 | ; randn ; todo generalize 229 | (define-py numpy.random.randn (~fun ~py -> ~py)) 230 | 231 | 232 | ; numpy.zeros(shape, dtype=float, order='C', *, like=None) 233 | ; Return a new array of given shape and type, filled with zeros. 234 | (define-py numpy.zeros (~fun ~shape #:dtype ~dtype -> ~ndarray)) 235 | 236 | (define-py numpy.ndarray.tolist (~fun ~ndarray -> ~py)) 237 | ; (define-py numpy.ndarray.item (~fun ~py -> ~py)) 238 | 239 | ;;; Numpy - Array Creation 240 | 241 | (define-py numpy.empty (~fun ~shape #:dtype ~dtype #:order ~order -> ~ndarray)) 242 | 243 | #;(define-py numpy.eye (~fun ~int #:dtype ~dtype #:order ~order -> ~ndarray)) 244 | #;(define-py numpy.shape (~fun ~ndarray -> ~tuple)) 245 | 246 | #;(define-py numpy.multiply (~fun ~py ~py -> ~py)) 247 | 248 | ; numpy.count_nonzero(a, axis=None, *, keepdims=False) 249 | #;(define-py numpy.count_nonzero (~fun ~py #:axis ~py #:keepdims ~py -> ~py)) 250 | 251 | 252 | ;;; 253 | ;;; Numpy `numpy.random` 254 | ;;; 255 | 256 | ; random.seed(self, seed=None) 257 | (define-py numpy.random.seed (~fun ~py -> ~py)) 258 | ; random.choice(a, size=None, replace=True, p=None) 259 | (define-py numpy.random.choice (~fun ~py #:size ~py #:replace ~py #:p ~py -> ~py)) 260 | 261 | 262 | ;;; 263 | ;;; Numpy FFT 264 | ;;; 265 | 266 | ; From the `numpy.fft` module 267 | 268 | ; fft.fft(a, n=None, axis=- 1, norm=None) 269 | #;(define-py numpy.fft.fft (~fun ~py ; a 270 | #:n ~py 271 | #:axis ~py 272 | #:norm ~py 273 | -> ~py)) 274 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/parameters.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide current-repr 3 | current-str 4 | current-pygenerator-prop:sequence) 5 | 6 | (define current-repr (make-parameter (λ (x) "current-repr not set to `repr` yet"))) 7 | (define current-str (make-parameter (λ (x) "current-str not set to `str` yet"))) 8 | 9 | (define current-pygenerator-prop:sequence 10 | ; used for prop:sequence of generator-obj structures, see "python-generator.rkt" 11 | (make-parameter (λ (x) "current-pygenerator-prop:sequence not set yet"))) 12 | 13 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/prefix-in.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base syntax/parse racket/require-transform)) 3 | 4 | (provide prefix-in) 5 | 6 | (define-syntax prefix-in 7 | (make-require-transformer 8 | (lambda (stx) 9 | (syntax-parse stx 10 | [(_ rel-path:string in:string ...) 11 | (define rp (syntax->datum #'rel-path)) 12 | (define ins (syntax->datum #'(in ...))) 13 | (define rp/ins (for/list ([i ins]) (string-append rp "/" i))) 14 | (with-syntax ([(rp/in ...) (datum->syntax stx rp/ins)]) 15 | (expand-import #`(combine-in rp/in ...)))])))) 16 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-attributes.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (rename-out [.app #%app])) 3 | (provide (rename-out [.top #%top])) 4 | (provide declare-special-prefix) 5 | 6 | 7 | (require (for-syntax racket/base racket/syntax syntax/parse racket/string racket/list)) 8 | (require (only-in "python-builtins.rkt" getattr)) 9 | (require (only-in "python-environment.rkt" get)) 10 | (require (only-in "python-types.rkt" pr)) 11 | (require (only-in "python-delayed.rkt" add-initialization-thunk)) 12 | (require (only-in "python-list.rkt" pylist)) 13 | (require (only-in "python-object.rkt" fast-obj-eq?)) 14 | (require "structs.rkt") 15 | (require racket/match racket/format) 16 | 17 | ;; getattr(object, name[, default]) 18 | 19 | ;; Return the value of the named attribute of object. Here `name` must be a 20 | ;; string. If the string is the name of one of the object’s attributes, 21 | ;; the result is the value of that attribute. For example, getattr(x, 22 | ;; 'foobar') is equivalent to x.foobar. If the named attribute does not 23 | ;; exist, default is returned if provided, otherwise AttributeError is 24 | ;; raised. 25 | 26 | ;; Instead of writing (getattr obj name #f) we want to write (.name obj). 27 | ;; Instead of defining a .name for each attribute, we are going to 28 | ;; 1. Let (#%app .name 29 | 30 | (define (get-obj qualified-name) 31 | (pr (get qualified-name))) 32 | 33 | 34 | (begin-for-syntax 35 | (define (identifier->string id) 36 | (cond 37 | [(string? id) id] 38 | [(symbol? id) (symbol->string id)] 39 | [else (symbol->string (syntax-e id))])) 40 | 41 | (define (dot-identifier? id) 42 | (eqv? (string-ref (identifier->string id) 0) #\.)) 43 | 44 | (define (identifier-split id sep) 45 | ; Like string-split, but for identifiers. 46 | ; Returns a syntax object with a list of strings. 47 | (define str (identifier->string id)) 48 | (define parts (string-split str sep)) 49 | (define ctx id) 50 | (define srcloc id) 51 | (define (-> x) (datum->syntax ctx x srcloc)) 52 | (-> (map -> parts))) 53 | 54 | (define (identifier-contains? id contained-str) 55 | (string-contains? (identifier->string id) 56 | contained-str)) 57 | 58 | (define (identifier-begins-with? id start-ch) 59 | (unless (char? start-ch) 60 | (error 'identifier-begins-with? "expected a character as start-ch")) 61 | (define str (identifier->string id)) 62 | (and (not (zero? (string-length str))) 63 | (eqv? (string-ref str 0) start-ch))) 64 | 65 | (define (method-identifier? id) 66 | (and (identifier-begins-with? id #\.) 67 | (not (identifier-contains? (identifier-drop-start id) ".")))) 68 | 69 | (define (identifier-drop-start id) 70 | (define str (identifier->string id)) 71 | (define sym (string->symbol (substring str 1 (string-length str)))) 72 | (datum->syntax id sym id id)) 73 | 74 | (define (identifier-append ctx srcloc . ids) 75 | (define (-> x) (datum->syntax ctx x srcloc)) 76 | (-> (string->symbol (string-append* (map identifier->string ids))))) 77 | 78 | (define (identifier-append* ctx srcloc ids) 79 | (apply identifier-append ctx srcloc ids)) 80 | 81 | (define (identifers->dotted-name id ids) 82 | (identifier-append* id id (add-between ids #'|.|))) 83 | 84 | (define (dotted-identifier->identifiers id) 85 | (define (-> x) (datum->syntax id x id)) 86 | (define strs (string-split (identifier->string #'id) ".")) 87 | (define syms (map string->symbol strs)) 88 | (map -> syms)) 89 | 90 | (define-syntax-class name ; an identifier without dots 91 | (pattern name:id 92 | #:when (not (identifier-contains? #'name ".")))) 93 | 94 | (define-syntax-class method ; an identifier that begins with a dot 95 | (pattern name:id 96 | #:when (identifier-begins-with? #'name #\.))) 97 | 98 | (define-syntax-class non-method 99 | (pattern (~not expr:method))) 100 | 101 | (define-syntax-class non-method-id ; an identifier that does not begin with a dot 102 | (pattern name:id 103 | #:when (not (identifier-begins-with? #'name #\.)))) 104 | 105 | (define-syntax-class dotted-name 106 | (pattern dotted-name:id 107 | #:when (identifier-contains? #'name ".") 108 | #:attr names (identifier-split #'dotted-name ".")))) 109 | 110 | (define-syntax (get-dotted stx) 111 | (syntax-parse stx 112 | [(_get-dotted id id0) 113 | #'id0] 114 | [(_get-dotted id id0 . ids) 115 | (define dotted-name (identifers->dotted-name #'id (syntax->list #'ids))) 116 | (with-syntax ([dotted-name (symbol->string (syntax-e dotted-name))]) 117 | (syntax/loc stx 118 | (let ([v id0]) 119 | (cond [(module? v) (get-obj 'id)] 120 | [(obj? v) (getattr v 'dotted-name #f)] 121 | [else id]))))])) 122 | 123 | 124 | ;;; Special Prefixes 125 | 126 | ; If a name is a special prefix 127 | 128 | (begin-for-syntax 129 | (define special-prefixes '()) ; list of symbols 130 | (set! special-prefixes '()) 131 | 132 | (define (set-special-prefixes value) 133 | (set! special-prefixes value)) 134 | 135 | (define (special? id) 136 | (define sym (syntax-e id)) 137 | (and (member sym special-prefixes) #t))) 138 | 139 | (define-syntax (declare-special-prefix stx) 140 | (syntax-parse stx 141 | [(_declare-special-prefix name ...) 142 | #'(begin-for-syntax 143 | (for ([sym (syntax->datum #'(name ...))]) 144 | (set-special-prefixes (cons sym special-prefixes))))])) 145 | 146 | 147 | 148 | (provide test-special) 149 | (define-syntax (test-special stx) 150 | (syntax-parse stx 151 | [(_test-special id) 152 | (if (special? #'id) 153 | #''special 154 | #''not-special)])) 155 | 156 | 157 | (define-syntax (.app stx) 158 | (syntax-parse stx 159 | [(_.app id:method e:expr (~optional default:expr #:defaults ([default #'#f]))) 160 | #;(displayln (list 'A stx)) 161 | (with-syntax ([name (identifier->string (identifier-drop-start #'id))]) 162 | (syntax/loc stx 163 | (getattr e name default)))] 164 | 165 | [(_.app id:dotted-name method:method . args) 166 | #;(displayln (list 'B stx)) 167 | (with-syntax ([(id0 id1 ...) (dotted-identifier->identifiers #'id)]) 168 | (syntax/loc stx 169 | (let ([o (get-dotted id id0 id1 ...)]) 170 | ((.app method o) . args))))] 171 | 172 | [(_.app id:dotted-name . args) 173 | #;(displayln (list 'C stx)) 174 | (with-syntax ([(id0 id1 ...) (dotted-identifier->identifiers #'id)]) 175 | (syntax/loc stx 176 | (let ([o (get-dotted id id0 id1 ...)]) 177 | (o . args))))] 178 | 179 | [(_.app e:expr method:method arg:non-method ...) 180 | (syntax/loc stx 181 | ((.app method e) arg ...))] 182 | 183 | [(_.app e:expr method:method arg:non-method ... method2:method . more) 184 | (syntax/loc stx 185 | (let ([o ((.app method e) arg ...)]) 186 | (.app o method2 . more)))] 187 | 188 | [(_.app e:expr arg:non-method ...) 189 | #;(displayln (list 'E stx)) 190 | (syntax/loc stx 191 | (e arg ...))])) 192 | 193 | 194 | 195 | (define (module? x) 196 | (and (obj? x) 197 | (member (obj-type-name x) '("module" "_automodule")) 198 | (not (obj-the-obj x)) ; #f = null 199 | #t)) 200 | 201 | (define missing #f) 202 | (add-initialization-thunk 203 | (λ () (set! missing (pylist "missing")))) 204 | 205 | 206 | (define (smart-get v id strs non-module/object-thunk) 207 | (define (error-no-attribute str) 208 | (raise-syntax-error (syntax-e id) 209 | (~a "object has no such attribute: " str) 210 | id)) 211 | 212 | (match strs 213 | ['() v] 214 | [(list str) (cond 215 | [(module? v) (getattr v str #f)] 216 | [(obj? v) (define res (getattr v str missing)) 217 | (when (and (obj? res) (fast-obj-eq? res missing)) 218 | (error-no-attribute str)) 219 | res] 220 | [else (non-module/object-thunk)])] 221 | [(list* str strs) (smart-get (smart-get v id (list str) non-module/object-thunk) 222 | id strs non-module/object-thunk)])) 223 | 224 | 225 | (define-syntax (.top stx) 226 | ; like #%top, but dotted identifiers are python qualified references 227 | (syntax-parse stx 228 | [(_.top . id:id) 229 | #;(displayln (list '.top #'id)) 230 | (cond 231 | [(identifier-begins-with? #'id #\.) 232 | #'(#%top . id)] 233 | 234 | [(identifier-contains? #'id ".") 235 | (when (identifier-contains? #'id "..") 236 | (raise-syntax-error '.top "two consecutive dots not allowed" #'id)) 237 | ; (define (-> x) (datum->syntax stx x #'id)) 238 | (define strs (syntax->datum (identifier-split #'id "."))) 239 | (define str0 (first strs)) 240 | (with-syntax ([id0 (datum->syntax #'id (string->symbol str0))] 241 | [(str1 ...) (rest strs)]) 242 | (syntax/loc stx 243 | (let ([v id0]) 244 | (smart-get v #'id (list str1 ...) (λ () '(#%top . id))))))] 245 | 246 | [else 247 | ; (displayln #'id) 248 | #'(#%top . id)])])) 249 | 250 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-builtins.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | ;;; 3 | ;;; Builtins 4 | ;;; 5 | 6 | ; This module provides bindings for the functions in the Python module `builtins`. 7 | 8 | (require "structs.rkt" 9 | "parameters.rkt" 10 | "python-c-api.rkt" 11 | "python-constants.rkt" 12 | "python-delayed.rkt" 13 | ; (rename-in (except-in "python-types.rkt" ~py) [~PY ~py]) 14 | "python-types.rkt" 15 | 16 | racket/format) 17 | 18 | ; (define-py builtins.repr (~fun ~obj -> ~string)) 19 | 20 | (define-py aiter (~fun ~py -> ~py) #:from builtins) 21 | (define-py all (~fun ~py -> ~bool) #:from builtins) 22 | ; anext 23 | (define-py any (~fun ~py -> ~bool) #:from builtins) 24 | (define-py ascii (~fun ~py -> ~string) #:from builtins) 25 | (define-py bin (~fun ~py -> ~string) #:from builtins) 26 | (define-py bool (~fun ~py -> ~bool) #:from builtins) ; todo: arg is optional 27 | ; breakpoint (not relevant in an embedding) 28 | (define-py bytearray (~fun ~py ~py ~py -> ~obj #:first-optional 0) #:from builtins) 29 | (define-py bytes (~fun ~py ~py ~py -> ~obj #:first-optional 0) #:from builtins) ; note, todo nameclash 30 | (define-py callable (~fun ~py -> ~bool) #:from builtins) 31 | (define-py chr (~fun ~int -> ~string) #:from builtins) 32 | ; @classmethod 33 | ; compile 34 | (define-py complex (~fun ~py ~py -> ~obj #:first-optional 0) #:from builtins) ; complex class 35 | (define-py delattr (~fun ~obj ~string -> ~None) #:from builtins) 36 | 37 | (provide dict) 38 | (define (dict x) 39 | (cond 40 | [(hash? x) (define d (PyDict_New)) 41 | (for ([(k v) (in-hash x)]) 42 | (PyDict_SetItem d (racket->python k) (racket->python v))) 43 | (obj "dict" d)] 44 | [else (error 'dict (~a "expected a hash, got: " x))])) 45 | 46 | ; dir is in "python-more-builtins.rkt" 47 | ; (define-py dir (~fun ~py -> ~py #;~list #:first-optional 0) #:from builtins) 48 | 49 | (define-py divmod (~fun ~py ~py -> ~py) #:from builtins) 50 | (define-py enumerate (~fun ~py #:start ~int -> ~obj) #:from builtins) 51 | ; (define-py builtins.eval (~fun ~py ~py ~py #:first-optional 1 -> ~py)) ; ? 52 | ; exec 53 | ; (define-py builtins.filter (~fun ~py ~py -> ~obj)) ; todo: callbacks ? 54 | ; filter 55 | (define-py builtins.float (~fun ~py -> ~float #:first-optional 0)) 56 | (define-py builtins.format (~fun ~py ~py -> ~py #:first-optional 1)) 57 | (define-py frozenset (~fun ~py -> ~py #:first-optional 0) #:from builtins) 58 | (define-py getattr (~fun ~py ~py ~py -> ~py #:first-optional 2) #:from builtins) 59 | ;(define-py builtins.globals (~fun -> ~obj)) ; todo 60 | (define-py hasattr (~fun ~py ~py -> ~bool) #:from builtins) 61 | (define-py builtins.hash (~fun ~py -> ~int)) 62 | ; help ; for interactive use 63 | (define-py hex (~fun ~py -> ~py) #:from builtins) 64 | (define-py builtins.id (~fun ~py -> ~py)) 65 | ; input ; for interactive use 66 | (define-py builtins.int (~fun ~py #:base ~int -> ~int #:first-optional 0)) 67 | (define-py isinstance (~fun ~py ~py -> ~py) #:from builtins) 68 | (define-py issubclass (~fun ~py ~py -> ~py) #:from builtins) 69 | (define-py iter (~fun ~py ~py -> ~py #:first-optional 1) #:from builtins) 70 | (define-py len (~fun ~py -> ~int) #:from builtins) 71 | (define-py builtins.list (~fun ~py -> ~py #:first-optional 0)) 72 | ;(define-py builtins.locals (~fun -> ~obj)) ; todo 73 | ; map ; todo: callback, callable 74 | (define-py builtins.max (~fun ~py -> ~py #:first-optional 0)) 75 | ; max ; todo 76 | ; memoryview ; todo 77 | ; min ; todo 78 | (define-py next (~fun ~py ~py -> ~py #:first-optional 1) #:from builtins) 79 | (define-py object ~obj #:from builtins) 80 | (define-py oct (~fun ~py -> ~py) #:from builtins) 81 | ; open ; todo file related 82 | (define-py ord (~fun ~char -> ~int) #:from builtins) 83 | (define-py builtins.pow (~fun ~py ~py ~py -> ~py #:first-optional 2)) 84 | ; Note: The two argument version of pow is equivalent to operator.pow i.e. to base**exp 85 | ; Note: The operator version is given the name pow. 86 | ; print ; todo i/o 87 | ; property ; todo 88 | (define-py builtins.range (~fun ~py ~py ~py -> ~py #:first-optional 1)) ; todo: use ~class 89 | (define-py repr (~fun ~py -> ~string) #:from builtins) 90 | (add-initialization-thunk (λ () (current-repr repr))) 91 | (define-py reversed (~fun ~py -> ~py) #:from builtins) 92 | (define-py builtins.round (~fun ~py ~py -> ~py #:first-optional 1)) 93 | (define-py builtins.set (~fun ~py -> ~py #:first-optional 0)) ; todo: use ~class 94 | ;(define-py setattr (~fun ~py ~py ~py -> ~py) #:from builtins) 95 | (define-py builtins.slice (~fun ~py ~py ~py -> ~py)) 96 | (provide slice-obj?) 97 | (define (slice-obj? x) 98 | (and (obj? x) (equal? (obj-type-name x) "slice"))) 99 | 100 | (define-py sorted (~fun ~py #:key ~py #:reverse ~py -> ~list) #:from builtins) 101 | ; staticmethod ; todo 102 | (define-py str (~fun ~py ~py ~py -> ~py #:first-optional 1) #:from builtins) ; todo use ~class 103 | (add-initialization-thunk (λ () (current-str str))) 104 | (define-py sum (~fun ~py #:start ~py -> ~py) #:from builtins) 105 | ; super ; todo 106 | (define-py builtins.tuple (~fun ~py -> ~py #:first-optional 0) ) ; todo: use ~class 107 | 108 | ; type ; todo 109 | (define-py vars (~fun ~py -> ~py #:first-optional 0) #:from builtins) 110 | ; zip ; todo 111 | ; __import__ ; todo 112 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-bytes.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (require "structs.rkt" 6 | "python-c-api.rkt" 7 | "python-environment.rkt" 8 | "python-operators.rkt" 9 | "python-slice.rkt" 10 | "python-types.rkt") 11 | 12 | (require racket/format) 13 | 14 | (require (for-syntax racket/base syntax/parse racket/syntax)) 15 | 16 | ;;; 17 | ;;; Byte Strings (Sequences of bytes) 18 | ;;; 19 | 20 | (define (pybytes? x) 21 | (and (obj? x) (equal? (obj-type-name x) "bytes"))) 22 | 23 | (define (pybytes . bs) 24 | (unless (andmap byte? bs) 25 | (raise-arguments-error 'pybytes "expected bytes as input" 26 | "bs" bs)) 27 | (bytes->pybytes (apply bytes bs))) 28 | 29 | (define (bytes->pybytes x) 30 | (unless (bytes? x) (error 'bytes->pybytes "got: ~a" x)) 31 | (obj "bytes" (PyBytes_FromStringAndSize x (bytes-length x)))) 32 | 33 | (define (pybytes->bytes x) 34 | (unless (pybytes? x) (error 'pybytes->bytes "got: ~a" x)) 35 | (PyBytes_AsString (obj-the-obj x))) 36 | 37 | (define (pybytes-length x) 38 | (unless (pybytes? x) (error 'pybytes-length "got: ~a" x)) 39 | (PyObject_Length (obj-the-obj x))) 40 | 41 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-class.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | ;;; 3 | ;;; Python Classes 4 | ;;; 5 | 6 | ; The class statement produces a class object. 7 | 8 | ; The type of a class object is `type`. 9 | 10 | 11 | ; Class objects support two kinds of operations: attribute references and instantiation. 12 | 13 | ; - Attribute references use the standard syntax used for all attribute references in Python: obj.name. 14 | ; That is: we can use `getattr` to implement obj.name where obj is a class object. 15 | ; - Class attributes can be assigned to. 16 | 17 | ; - Class instantiation uses function notation. 18 | ; Any arguments given are passed on to the method `__init__`. 19 | ; - Signature: 20 | 21 | ; Instance objects support: 22 | ; - attribute reference 23 | 24 | ; An attribute reference can be a "data attribute reference" or a "method attribute reference". 25 | 26 | ; By definition, all attributes of a class that are function objects define corresponding methods of its instances. 27 | 28 | ; Note if `c` is a class and `i` is an instance of c`: then if `c.f` is a function, then `i.f` is a method object. 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-constants.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide initialize-builtin-constants 3 | True False None) 4 | 5 | (require "python-evaluation.rkt") 6 | 7 | (define True #f) 8 | (define False #f) 9 | (define None #f) 10 | 11 | (define (initialize-builtin-constants) 12 | (set! True (run "True")) ; Note: These can't be returned directly 13 | (set! False (run "False")) ; Due to reference counting, 14 | (set! None (run "None"))) 15 | 16 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-define-delayed.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "python-delayed.rkt" 3 | "python-environment.rkt") 4 | 5 | (require (for-syntax racket/base 6 | racket/format 7 | racket/match 8 | racket/syntax 9 | syntax/parse)) 10 | 11 | ;;; 12 | ;;; SYNTAX (define/delay racket-id qualified-name expr ...) 13 | ;;; 14 | 15 | ; If `qualified-name` is bound in the Python environment, 16 | ; evaluate the expressions `expr ...` and bind the result to `racket-id`. 17 | ; If `qualified-name` is unbound in the Python environment, 18 | ; then bind `racket-id` to `'` and add a thunk (to the list 19 | ; of delayed initialization thunks) that when called evaluates the 20 | ; expressions `expr ...` and stores the result in `racket-id`. 21 | 22 | ; Typical usage: Extract a value/function from the Python environment, 23 | ; if the value is ready. Otherwise, store a think that 24 | ; can extract the value, when it is ready. 25 | 26 | (provide define/delay) 27 | (define-syntax (define/delay stx) 28 | (syntax-parse stx 29 | [(_define/delay racket-id:id qualified-name:id expr:expr ...) 30 | (syntax/loc stx 31 | (begin 32 | (provide racket-id) 33 | (define racket-id ') 34 | (define (init-thunk) 35 | (set! racket-id (let () expr ...))) 36 | (if (id-bound? 'qualified-name) 37 | (init-thunk) 38 | (add-initialization-thunk init-thunk))))])) 39 | 40 | ;;; 41 | ;;; SYNTAX (define-delayed 42 | ;;; (define racket-id expr) 43 | ;;; ...) 44 | ;;; 45 | 46 | ;; Wrap a series of `(define racket-id expr)` in `(define-delayed ...)` 47 | ;; in order to turn them into `(define/delay racket-id racket-id expr)`. 48 | 49 | (provide define-delayed) 50 | (define-syntax (define-delayed stx) 51 | ; When the racket-id and the qualified-name are the same, 52 | ; we can wrap a sequence of definitions into `define-delayed`. 53 | (syntax-parse stx 54 | [(_define-delayed 55 | (define racket-id:id expr:expr) 56 | ...) 57 | (syntax/loc stx 58 | (begin 59 | (provide racket-id ...) 60 | (define/delay racket-id racket-id expr) ...))])) 61 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-delayed.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide run-initialization-thunks 3 | add-initialization-thunk) 4 | 5 | ;; The forms `define-delayed` and `define/delay` are in "python-define-delayed.rkt". 6 | 7 | 8 | (define initialization-thunks '()) 9 | 10 | (define (add-initialization-thunk thunk) 11 | (set! initialization-thunks 12 | (cons thunk initialization-thunks))) 13 | 14 | (define (run-initialization-thunks) 15 | (for ([t (in-list (reverse initialization-thunks))]) 16 | (t)) 17 | (set! initialization-thunks '())) 18 | 19 | 20 | 21 | (require (for-syntax racket/base 22 | racket/syntax 23 | syntax/parse)) 24 | 25 | 26 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-dict.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (all-defined-out)) 3 | 4 | (require "structs.rkt" 5 | "python-c-api.rkt" 6 | "python-environment.rkt" 7 | "python-types.rkt") 8 | 9 | (require racket/match racket/format) 10 | 11 | (require (for-syntax racket/base syntax/parse racket/syntax)) 12 | 13 | ;;; 14 | ;;; Mapping Protocol 15 | ;;; 16 | 17 | (define (mapping? x) 18 | (and (obj? x) 19 | (let () 20 | (define o (obj-the-obj x)) 21 | (define ok (PyMapping_Check o)) 22 | (equal? ok 1)))) 23 | 24 | ;;; 25 | ;;; Dictionaries (pydict) 26 | ;;; 27 | 28 | (define (pydict? x) 29 | (and (obj? x) 30 | (or (equal? (obj-type-name x) "dict") 31 | (mapping? x)))) 32 | 33 | (define (pydict-new) 34 | (define d (PyDict_New)) ; new reference 35 | (obj "dict" d)) 36 | 37 | (define (pydict-proxy-new mapping) 38 | ; creates read-only dict 39 | (unless (mapping? mapping) 40 | (raise-arguments-error 'pydict-proxy-new "expected a mapping" "mapping" mapping)) 41 | 42 | (define o (obj-the-obj mapping)) 43 | (define p (PyDictProxy_New o)) ; new reference 44 | (obj "mappingproxy" p)) 45 | 46 | (define (pydict-clear! d) ; remove all keys 47 | (unless (pydict? d) 48 | (raise-arguments-error 'dict-clear! "expected a dict" "dict" d)) 49 | 50 | (define o (obj-the-obj d)) 51 | (void (PyDict_Clear o))) 52 | 53 | (define (pydict-contains? dict key) ; equivalent to "key in dict" 54 | (unless (pydict? dict) 55 | (raise-arguments-error 'pydict-contains? "expected a dict" "dict" dict)) 56 | 57 | (define d (obj-the-obj dict)) 58 | (define k (racket->python key)) 59 | (case (PyDict_Contains d k) 60 | [(1) #t] 61 | [(0) #f] 62 | [else (raise-arguments-error 'pydict-contains? "an error occurred")])) 63 | 64 | (define (pydict-copy x) 65 | (unless (pydict? x) 66 | (raise-arguments-error 'pydict-copy "expected a dict" "dict" x)) 67 | 68 | (define o (obj-the-obj x)) 69 | (obj "dict" (PyDict_Copy o))) 70 | 71 | (define (pydict-set! dict key val) 72 | (define who 'pydict-set!) 73 | (unless (pydict? dict) 74 | (raise-arguments-error who "expected a dict" "dict" dict)) 75 | 76 | (define d (obj-the-obj dict)) 77 | (define v (racket->python val)) 78 | 79 | (cond 80 | [(string? key) ; fast path 81 | (case (PyDict_SetItemString d key v) 82 | [(0) (void)] ; success 83 | [else (error who "error in call to PyDict_SetItemString")])] 84 | [else 85 | (define k (racket->python key)) 86 | (case (PyDict_SetItem d k v) 87 | [(0) (void)] ; success 88 | [else (error who "error in call to PyDict_SetItem")])])) 89 | 90 | (define (pydict-remove! dict key) ; delitem 91 | (define who 'pydict-remove!) 92 | (unless (pydict? dict) 93 | (raise-arguments-error who "expected a dict" "dict" dict)) 94 | 95 | (define d (obj-the-obj dict)) 96 | (cond 97 | [(string? key) ; fast path 98 | (case (PyDict_DelItemString d key) 99 | [(0) (void)] ; succes 100 | [else (PyErr_Clear) ; KeyError is thrown if item is not in the dict 101 | (void)])] 102 | [else 103 | (define k (racket->python key)) 104 | (case (PyDict_DelItem d k) 105 | [(0) (void)] ; succes 106 | [else (PyErr_Clear) ; KeyError is thrown if item is not in the dict 107 | (void)])])) 108 | 109 | (define (~w x) 110 | (let ([o (open-output-string)]) 111 | (write x o) 112 | (get-output-string o))) 113 | 114 | 115 | (define (pydict-ref dict key 116 | [failure-result 117 | (λ () 118 | (raise (make-exn:fail:contract 119 | (~a "pydict-ref: no value found for key\n key: " (~w key)) 120 | (current-continuation-marks))))]) 121 | (unless (pydict? dict) 122 | (raise-arguments-error 'pydict-ref "expected a dict" "dict" dict)) 123 | 124 | (define d (obj-the-obj dict)) 125 | (cond 126 | [(string? key) ; fast path 127 | (define v (PyDict_GetItemString d key)) ; never raises exceptions 128 | (cond 129 | [v (pr v)] 130 | [(procedure? failure-result) (failure-result)] 131 | [else failure-result])] 132 | [else 133 | (define k (rp key)) 134 | (define v (PyDict_GetItem d k)) 135 | (cond 136 | [v (pr v)] 137 | [(procedure? failure-result) (failure-result)] 138 | [else failure-result])])) 139 | 140 | (define (pydict->hash x 141 | #:convert-key [convert-key pr/key] 142 | #:convert-value [convert-value pr]) 143 | (define o (obj-the-obj x)) 144 | (define vs (PyDict_Keys o)) ; pylist 145 | (define n (PyList_Size vs)) 146 | (for/hash ([i (in-range n)]) 147 | (define k (PyList_GetItem vs i)) 148 | (when (eqv? k #f) (PyErr_Clear)) 149 | (define key (and k (convert-key k))) 150 | 151 | (define v (and k (PyDict_GetItem o k))) 152 | (when (and k (eqv? v #f)) (PyErr_Clear)) 153 | (define val (and k v (convert-value v))) 154 | 155 | (values key val))) 156 | 157 | (define (hash->pydict x #:convert [convert rp]) 158 | (define who 'hash->pydict) 159 | (unless (hash? x) 160 | (raise-arguments-error who "expected a hash table" "hash" x)) 161 | 162 | (define d (PyDict_New)) 163 | (for ([(k v) (in-hash x)]) 164 | (cond 165 | [(string? k) ; fast path 166 | (case (PyDict_SetItemString d k (convert v)) 167 | [(0) (void)] ; succes 168 | [else (error who "error during call to PyDict_SetItemString")])] 169 | [else 170 | (case (PyDict_SetItem d (convert k) (convert v)) 171 | [(0) (void)] ; succes 172 | [else (error who "error during call to PyDict_SetItem")])])) 173 | (obj "dict" d)) 174 | 175 | (define (pydict #:convert [convert rp] . args) 176 | (define who 'pydict) 177 | (define n (length args)) 178 | (unless (even? n) 179 | (raise-arguments-error who "expected an even number of arguments" "keys and values" args)) 180 | 181 | (define d (PyDict_New)) ; new reference 182 | 183 | (let loop ([as args]) 184 | (match as 185 | ['() (void)] 186 | [(list* k v as) 187 | (define val (convert v)) 188 | (cond 189 | [(string? k) ; fast path 190 | (case (PyDict_SetItemString d k val) 191 | [(0) (void)] ; succes 192 | [else (error who "error during call to PyDict_SetItemString")])] 193 | [else 194 | (define key (convert k)) 195 | (case (PyDict_SetItem d key val) 196 | [(0) (void)] ; succes 197 | [else (error who "error during call to PyDict_SetItem")])]) 198 | (loop as)])) 199 | (obj "dict" d)) 200 | 201 | 202 | (define (pydict-keys x) 203 | (unless (pydict? x) 204 | (raise-arguments-error 'pydict-keys "expected a dict" "dict" x)) 205 | 206 | (define o (obj-the-obj x)) 207 | (define ks (PyDict_Keys o)) ; new reference 208 | (obj "list" ks)) 209 | 210 | (define (pydict-values x) 211 | (unless (pydict? x) 212 | (raise-arguments-error 'pydict-values "expected a dict" "dict" x)) 213 | 214 | (define o (obj-the-obj x)) 215 | (define vs (PyDict_Values o)) ; new reference 216 | (obj "list" vs)) 217 | 218 | (define (pydict-count x) 219 | (define who 'pydict-count) 220 | (unless (pydict? x) 221 | (raise-arguments-error who "expected a dict" "dict" x)) 222 | 223 | (define o (obj-the-obj x)) 224 | (define s (PyDict_Size o)) ; new reference 225 | s) 226 | 227 | (define (pydict-merge! a b [override #t]) 228 | (define who 'pydict-merge!) 229 | (unless (pydict? a) 230 | (raise-arguments-error who "expected a pydict as first argument" "a" a "b" b)) 231 | (unless (mapping? b) 232 | (raise-arguments-error who "expected a pydict as second argument" "a" a "b" b)) 233 | 234 | (define oa (obj-the-obj a)) 235 | (define ob (obj-the-obj b)) 236 | (case (PyDict_Merge oa ob (or (and override 1) 0)) 237 | [(0) (void)] 238 | [else (error who "an error occurred")])) ; todo 239 | 240 | (define (pydict-update! a b) 241 | (unless (pydict? a) 242 | (raise-arguments-error 'pydict-update! "expected a pydict as first argument" "a" a "b" b)) 243 | (unless (mapping? b) 244 | (raise-arguments-error 'pydict-update! "expected a pydict as second argument" "a" a "b" b)) 245 | 246 | (define oa (obj-the-obj a)) 247 | (define ob (obj-the-obj b)) 248 | (case (PyDict_Update oa ob) 249 | [(0) (void)] 250 | [else (error 'pydict-update! "an error occurred")])) 251 | 252 | (require (only-in ffi/unsafe malloc memset free)) 253 | 254 | ; The position is a pointer to an size_t integer initialized to zero. 255 | 256 | (define (pydict->key/values x) 257 | (define who 'pydict->key/values) 258 | (unless (pydict? x) 259 | (raise-arguments-error who "expected a pydict as argument" "d" x)) 260 | 261 | (define o (obj-the-obj x)) 262 | ; Allocate new position 263 | (define pos (malloc 'raw 16)) 264 | ; Initialize position to zero. 265 | (memset pos 0 16) 266 | ; loop until pos is zero again 267 | (let loop ([kvs '()]) 268 | (define flag/key/value (PyDict_Next o pos #f #f)) 269 | (case (car flag/key/value) 270 | [(0) (free pos) 271 | (reverse kvs)] 272 | [else (define kv (list (pr (cadr flag/key/value)) 273 | (pr (caddr flag/key/value)))) 274 | (loop (cons kv kvs))]))) 275 | 276 | (define (in-pydict pydict) 277 | (define o (obj-the-obj pydict)) 278 | (make-do-sequence 279 | (λ () (values (λ (pos) (values (pr (caddr pos)) (pr (cadddr pos)))) ; pos->elm 280 | #f ; update (identity) 281 | (λ (pos) (cons (car pos) (PyDict_Next o (car pos) #f #f))) ; next 282 | (let ([pos (malloc 'raw 16)]) ; initial-pos 283 | (memset pos 0 16) 284 | (cons pos (PyDict_Next o pos #f #f))) 285 | (λ (pos) ; continue with pos? 286 | (define cont? (not (= (cadr pos) 0))) 287 | (unless cont? (free (car pos))) 288 | cont?) 289 | #f 290 | #f)))) 291 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-environment.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide 4 | import-into-python 5 | initialize-main-and-builtins 6 | import-numpy 7 | 8 | main builtins globals locals get get* 9 | get-module 10 | id-bound?) 11 | 12 | (require "structs.rkt" 13 | "python-c-api.rkt" 14 | ; (only-in "python-builtins.rkt" getattr) 15 | racket/string racket/match racket/format) 16 | 17 | 18 | ; (define-py getattr (~fun ~py ~py ~py -> ~py #:first-optional 2) #:from builtins) 19 | 20 | 21 | (define main #f) 22 | (define builtins #f) 23 | (define globals #f) 24 | (define locals #f) 25 | 26 | (define mod:operator #f) 27 | ;(define mod:traceback #f) 28 | 29 | (define (initialize-main-and-builtins) 30 | ; (displayln (list 'initialize-main-and-builtins)) 31 | ; AddModule doesn't load or impor the module, it just returns it. 32 | (set! main (PyImport_AddModule "__main__")) ; top-level environment 33 | (set! builtins (PyImport_AddModule "builtins")) 34 | ; (PyImport_AddModule "operator") ; this leads to an error on Ubuntu 35 | ; (PyImport_AddModule "traceback") 36 | 37 | (set! globals (PyModule_GetDict main)) ; globally defined variables 38 | (set! locals (PyDict_New)) 39 | 40 | (define empty-from-list (PyList_New 0)) 41 | ; Add `builtins` and `operators` to the global symbol table 42 | ; (PyImport_ImportModuleEx "builtins" globals globals empty-from-list) 43 | (PyModule_AddObjectRef main "__builtins__" builtins) ; use the name "builtins" for the value builtins. 44 | (PyModule_AddObjectRef main "builtins" builtins) ; use the name "builtins" for the value builtins. 45 | 46 | (set! mod:operator 47 | (PyImport_ImportModuleEx "operator" globals globals empty-from-list)) 48 | (void (PyModule_AddObjectRef main "operator" mod:operator)) 49 | 50 | (import-into-python 'traceback) 51 | (import-into-python 'inspect) 52 | 53 | (void)) 54 | 55 | (define (import-into-python module-sym [as #f]) 56 | ; import `module-sym` into the Python environment 57 | (define module-str (~a module-sym)) 58 | (define empty-from-list (PyList_New 0)) 59 | (define mod (PyImport_ImportModuleEx module-str globals globals empty-from-list)) 60 | ; (displayln (list 'import-into-python (or (and as (~a as)) module-str))) 61 | (cond 62 | [mod (void (PyModule_AddObjectRef main (or (and as (~a as)) module-str) mod))] 63 | [else 64 | (PyErr_Print) 65 | (error 'import (~a "No Python module named '" module-sym "'"))])) 66 | 67 | (define (import-numpy) 68 | (define empty-from-list (PyList_New 0)) 69 | (define mod:numpy (PyImport_ImportModuleEx "numpy" globals globals empty-from-list)) 70 | (when mod:numpy 71 | (void (PyModule_AddObjectRef main "numpy" mod:numpy)))) ; 0=success 72 | 73 | 74 | (define (id-bound? sym [dict globals]) 75 | (and main 76 | (case sym 77 | [(main) #t] 78 | ; [(builtins) (and builtins #t)] 79 | [else (and dict 80 | (PyDict_GetItemString dict (~a sym)) 81 | #t)]))) 82 | 83 | 84 | (define (get sym [type #f]) 85 | ;; (newline) (displayln "--") (newline) 86 | ;; (displayln (list 'get 'sym: sym)) 87 | ;; (displayln (list 'main: main 'globals: globals)) 88 | ;; (displayln (list 'get sym type)) 89 | (define names (string-split (symbol->string sym) ".")) 90 | (match names 91 | [(list* "builtins" names*) 92 | ;(displayln "using builtins") 93 | ;(displayln (PyUnicode_AsUTF8 (PyObject_Str builtins))) 94 | ;(displayln (PyDict_GetItemString builtins "len")) 95 | (get* builtins names* names #f sym type)] 96 | [(list* "main" names*) 97 | (get* main names* names #f sym type)] 98 | [_ 99 | (get* globals names names #f sym type)])) 100 | 101 | 102 | 103 | (define (get* obj names all-names prefix orig-sym type) 104 | ; (begin (write (list 'get* obj names all-names orig-sym type)) (newline)) 105 | #;(displayln (PyUnicode_AsUTF8 (PyObject_Str obj))) 106 | ; obj is a either a module or a dict 107 | (match names 108 | [(list) 109 | (unless obj (error 'get (~a "not bound xx: " orig-sym))) 110 | obj] 111 | [(list* name names) ; reference to a global variable 112 | (define typename (python-type obj)) 113 | (define item 114 | (case typename 115 | ; Note: Use `getattr` here. If a package has "automodules" the 116 | ; dynamically generated modules don't show up in the dict 117 | ; associated with the module. 118 | ; Note: Benchmark: Would a fast path with dict and a fallback 119 | ; to `getattr` be faster? 120 | [("module" "_automodule") (PyObject_GetAttrString obj name)] 121 | [("dict") (PyDict_GetItemString obj name)] 122 | [("type") (PyObject_GetAttrString obj name)] 123 | [else 124 | (error 'get* (~a "got object of type : " typename))])) 125 | (unless item 126 | (error 'get (~a "not bound: yy " name " in " orig-sym))) 127 | (define new-prefix (if prefix (string-append prefix name ".") (string-append name "."))) 128 | (get* item names all-names new-prefix orig-sym type)])) 129 | 130 | 131 | (define (get-module sym) 132 | (define mod (get sym)) 133 | (obj "module" mod)) 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-evaluation.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide run 3 | run*) 4 | 5 | (require "structs.rkt" 6 | "python-c-api.rkt" 7 | "python-environment.rkt" 8 | racket/format) 9 | 10 | (require (for-syntax racket/base syntax/parse racket/format)) 11 | 12 | 13 | ; These values represent the start symbol from the Python grammar. 14 | (define Py_single_input 256) ; single statement, used for interactive interpreter loop 15 | (define Py_file_input 257) ; sequences of statements, used for files 16 | (define Py_eval_input 258) ; isolated expression, used with Py_CompileString 17 | 18 | 19 | (define (run code [result-type #f]) 20 | ; run: evaluates a single expression and returns the value 21 | (define result (PyRun_String (~a code "\n") Py_eval_input globals globals)) 22 | (if result-type 23 | (let ([to (pytype-python-to-racket result-type)]) 24 | (if to 25 | (to result) 26 | result)) 27 | result)) 28 | 29 | ; This version doesn't allow to set the source location 30 | #;(define (run* code) 31 | ; run*: evalutes a sequence of statements 32 | ; (write (~a code "\n\n")) (newline) (display (~a code "\n")) (newline) 33 | (PyRun_String (~a code "\n") Py_file_input globals globals)) 34 | 35 | #;(define (run* code [source-location-string ""]) 36 | ; run*: evalutes a sequence of statements 37 | ; (write (~a code "\n\n")) (newline) (display (~a code "\n")) (newline) 38 | (define code-object (Py_CompileString (~a code "\n") source-location-string Py_file_input)) 39 | (PyEval_EvalCode code-object globals globals)) 40 | 41 | 42 | (define-syntax (run* stx) 43 | (syntax-parse stx 44 | [(_run* code) 45 | (define source (syntax-source stx)) 46 | (define line (syntax-line stx)) 47 | (define col (syntax-column stx)) 48 | (define location (~a "<" (or source "") ":" (or line "") ":" (or col "") ">")) 49 | (with-syntax ([location location]) 50 | (syntax/loc stx 51 | (let ([code-object (Py_CompileString (~a code "\n") location Py_file_input)]) 52 | (when code-object 53 | (PyEval_EvalCode code-object globals globals)))))])) 54 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-generator.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (require "structs.rkt" 6 | "parameters.rkt" 7 | "python-c-api.rkt" 8 | "python-environment.rkt" 9 | "python-types.rkt" 10 | "python-attributes.rkt") 11 | 12 | (require (for-syntax racket/base syntax/parse racket/syntax)) 13 | 14 | 15 | (define (pygenerator? x) 16 | (and (obj? x) 17 | (or (equal? (obj-type-name x) "generator") 18 | #;(PyList_Check (obj-the-obj x))))) 19 | 20 | (define none-yet (list 'none-yet)) 21 | 22 | (define (in-pygenerator pygen) 23 | (let ([next-val none-yet]) 24 | (make-do-sequence 25 | (λ () (values (λ (pos) ; pos->element 26 | (begin0 27 | (if (eq? next-val none-yet) 28 | (pygen .__next__) 29 | next-val) 30 | (set! next-val (pygen .__next__)))) 31 | #f ; optional early-next-pos 32 | (λ (pos) pos) ; next-pos 33 | pygen ; initial pos 34 | (λ (pos) (not (eq? next-val 'StopIteration))) ; continue with pos 35 | #f 36 | #f))))) 37 | 38 | 39 | (current-pygenerator-prop:sequence in-pygenerator) 40 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-import.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide import 3 | import-from 4 | as) 5 | 6 | ;;; 7 | ;;; IMPORT 8 | ;;; 9 | 10 | ;; This module export the two forms `import` and `import-from`. 11 | ;; The intent is that they match the Python statements "import" and "from". 12 | 13 | ;; SYNTAX 14 | ;; (import dotted-name) 15 | ;; or (import dotted-name as as-name) 16 | ;; where `as` is a keyword. 17 | 18 | ;; Import `dotted-name` in the Python environment. 19 | ;; Get a module object and bind it to either `dotted-name` or `as-name`. 20 | 21 | 22 | ;; The Python docs say the following about the `from` statement: 23 | 24 | ;; The `from` form uses a slightly more complex process: 25 | 26 | ;; 1. find the module specified in the from clause, loading and initializing it if necessary; 27 | ;; 2. for each of the identifiers specified in the import clauses: 28 | ;; 1. check if the imported module has an attribute by that name 29 | ;; 2. if not, attempt to import a submodule with that name and then check 30 | ;; the imported module again for that attribute 31 | ;; 3. if the attribute is not found, ImportError is raised. 32 | ;; 4. otherwise, a reference to that value is stored in the local namespace, 33 | ;; using the name in the as clause if it is present, otherwise using the 34 | ;; attribute name 35 | 36 | 37 | (require ; "python.rkt" 38 | ; "python-c-api.rkt" 39 | "python-environment.rkt" 40 | "python-functions.rkt" 41 | "python-module.rkt" 42 | "python-types.rkt") 43 | 44 | (require (for-syntax ; (except-in "python.rkt" bytes #%app) 45 | "python-environment.rkt" 46 | racket/base 47 | racket/string 48 | (except-in syntax/parse str) 49 | racket/syntax)) 50 | 51 | (define (get-obj qualified-name) 52 | #;(displayln (list 'import-get-obj 'qualified-name qualified-name)) 53 | (pr (get qualified-name))) 54 | 55 | (define-syntax (as stx) (raise-syntax-error 'as "used outside import/import-from form" stx)) 56 | 57 | (begin-for-syntax 58 | (define (identifier->string id) 59 | (cond 60 | [(string? id) id] 61 | [(symbol? id) (symbol->string id)] 62 | [else (symbol->string (syntax-e id))])) 63 | 64 | (define (identifier-split id sep) 65 | ; Like string-split, but for identifiers. 66 | ; Returns a syntax object with a list of strings. 67 | (define str (identifier->string id)) 68 | (define parts (string-split str sep)) 69 | (define ctx id) 70 | (define srcloc id) 71 | (define (-> x) (datum->syntax ctx x srcloc)) 72 | (-> (map -> parts))) 73 | 74 | (define (identifier-contains? id contained-str) 75 | (string-contains? (identifier->string id) 76 | contained-str)) 77 | 78 | (define (identifier-append ctx srcloc . ids) 79 | (define (-> x) (datum->syntax ctx x srcloc)) 80 | (-> (string->symbol (string-append* (map identifier->string ids))))) 81 | 82 | (define-syntax-class name ; an identifier without dots 83 | (pattern name:id 84 | #:when (not (identifier-contains? #'name ".")))) 85 | 86 | (define-syntax-class dotted-name 87 | (pattern dotted-name:id 88 | #:attr names (identifier-split #'dotted-name ".")))) 89 | 90 | (define-syntax (import stx) 91 | (syntax-parse stx 92 | #:literals (as) 93 | [(_import (~seq dotted-name:dotted-name 94 | (~optional (~seq as as-name:name) #:defaults ([as-name #'#f]))) ...) 95 | (syntax/loc stx 96 | (begin (import-as-name dotted-name as-name) ...))])) 97 | 98 | (define-syntax (import-as-name stx) 99 | (syntax-parse stx 100 | #:literals (as) 101 | [(_import qualifier:dotted-name #f) 102 | (syntax/loc stx 103 | (begin 104 | (import-into-python 'qualifier) 105 | (define qualifier (get-obj 'qualifier))))] 106 | [(_import qualifier:dotted-name as-name:name) 107 | (syntax/loc stx 108 | (begin 109 | (import-into-python 'qualifier) 110 | (define as-name (get-obj 'qualifier))))])) 111 | 112 | (define-syntax (import-from stx) 113 | (syntax-parse stx 114 | #:literals (as) 115 | [(_import qualifier:dotted-name 116 | (~seq target:dotted-name (~optional (~seq as as-name:name) #:defaults ([as-name #'#f]))) 117 | ...) 118 | (syntax/loc stx 119 | (begin (import-from-as qualifier target as-name) ...))])) 120 | 121 | (define-syntax (import-from-as stx) 122 | (syntax-parse stx 123 | [(_import-from qualifier:dotted-name target:name #f) 124 | (with-syntax ([qualifier.target (identifier-append stx stx #'qualifier "." #'target)]) 125 | (syntax/loc stx 126 | (begin 127 | (import-into-python 'qualifier) 128 | (define target (get-obj 'qualifier.target)))))] 129 | [(_import-from qualifier:dotted-name target:name as-name:name) 130 | (with-syntax ([qualifier.target (identifier-append stx stx #'qualifier "." #'target)] 131 | #;[as-name (syntax-local-introduce #'as-name)]) 132 | (syntax/loc stx 133 | (begin 134 | (import-into-python 'qualifier.target) 135 | (define as-name (get-obj 'qualifier.target)))))])) 136 | 137 | 138 | ;; import foo # foo imported and bound locally 139 | ;; import foo.bar.baz # foo, foo.bar, and foo.bar.baz imported, foo bound locally 140 | ;; import foo.bar.baz as fbb # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb 141 | ;; from foo.bar import baz # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz 142 | ;; from foo import attr # foo imported and foo.attr bound as attr 143 | 144 | 145 | ;; import_stmt: import_name | import_from 146 | ;; import_name: 'import' dotted_as_names 147 | ;; # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS 148 | ;; import_from: 149 | ;; | 'from' ('.' | '...')* dotted_name 'import' import_from_targets 150 | ;; | 'from' ('.' | '...')+ 'import' import_from_targets 151 | ;; import_from_targets: 152 | ;; | '(' import_from_as_names [','] ')' 153 | ;; | import_from_as_names !',' 154 | ;; | '*' 155 | ;; import_from_as_names: 156 | ;; | ','.import_from_as_name+ 157 | ;; import_from_as_name: 158 | ;; | NAME ['as' NAME ] 159 | ;; dotted_as_names: 160 | ;; | ','.dotted_as_name+ 161 | ;; dotted_as_name: 162 | ;; | dotted_name ['as' NAME ] 163 | ;; dotted_name: 164 | ;; | dotted_name '.' NAME 165 | ;; | NAME 166 | 167 | 168 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-initialization.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "libpython.rkt" 3 | "python-c-api.rkt" 4 | "python-environment.rkt" 5 | "python-delayed.rkt" 6 | "python-constants.rkt" 7 | "structs.rkt" 8 | racket/file) 9 | 10 | (provide set-environment-variables 11 | initialize 12 | post-initialize finish-initialization 13 | diagnostics) 14 | 15 | ;;; 16 | ;;; Configuration 17 | ;;; 18 | 19 | ; (define program-full-path "/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10") 20 | (define program-full-path "python3.10") 21 | 22 | (define home (get-preference 'pyffi:data (λ () #f))) 23 | (unless home 24 | (parameterize ([current-output-port (current-error-port)]) 25 | (displayln "There is no preference for 'pyffi:data' set.") 26 | (displayln "You must set the 'pyffi:data' preference to the home folder of python.") 27 | (displayln "The most convenient way to do this, is to run `raco pyffi configure`.") 28 | (displayln "See details in the documentation.") 29 | (exit 1))) 30 | 31 | 32 | #;(define program-full-path 33 | "/usr/local/Cellar/python@3.10/3.10.4/Frameworks/Python.framework/Versions/3.10/bin/python3.10") 34 | 35 | ; (define home "/usr/local/Cellar/python@3.10/3.10.4/Frameworks/Python.framework/Versions/3.10") 36 | 37 | 38 | (define libdir (get-preference 'pyffi:libdir (λ () #f))) 39 | (unless libdir 40 | (parameterize ([current-output-port (current-error-port)]) 41 | (displayln "There is no preference for 'pyffi:libdir' set.") 42 | (displayln "You must set the 'pyffi:libdir' preference to the home folder of python.") 43 | (displayln "The most convenient way to do this, is to run `raco pyffi configure`.") 44 | (displayln "See details in the documentation.") 45 | (exit 1))) 46 | 47 | (define (set-environment-variables) 48 | (define (decode s) (Py_DecodeLocale s #f)) 49 | (Py_SetProgramName (decode "python3.10")) 50 | ; (Py_SetProgramName (decode (build-path libdir))) 51 | ; (Py_SetPath (Py_DecodeLocale (get-preference 'pyffi:data (λ () #f)) #f)) 52 | (Py_SetPythonHome (decode home))) 53 | 54 | ;;; 55 | ;;; Diagnostics 56 | ;;; 57 | 58 | 59 | (define (diagnostics) 60 | (define (encode s) (and s (Py_EncodeLocale s #f))) ; wchar -> string 61 | (displayln (list 'ProgramName (encode (Py_GetProgramName)))) 62 | (displayln (list 'Prefix (encode (Py_GetPrefix)))) 63 | (displayln (list 'ExecPrefix (encode (Py_GetExecPrefix)))) 64 | (displayln (list 'ProgramFullPath (encode (Py_GetProgramFullPath)))) 65 | (displayln (list 'Path (encode (Py_GetPath)))) 66 | (displayln (list 'PythonHome (encode (Py_GetPythonHome))))) 67 | 68 | ;;; 69 | ;;; Setup Initial Environment 70 | ;;; 71 | ;;; 72 | ;;; Evaluation 73 | ;;; 74 | 75 | 76 | #;(define (initialize) 77 | (set-environment-variables) 78 | (Py_Initialize) 79 | (initialize-main-and-builtins) 80 | (initialize-builtin-constants) ; uses `run` 81 | ; We can't run the initialization thunks here. 82 | ; The Python modules are loaded yet. 83 | #;(run-initialization-thunks)) 84 | 85 | 86 | (require ffi/unsafe 87 | #;(only-in ffi/unsafe malloc cast _cpointer ptr-ref cpointer-tag cpointer-push-tag!)) 88 | 89 | 90 | (define (initialize) 91 | ; (set-environment-variables) 92 | ; (displayln PyConfig-tag) ; 'PyConfig 93 | ; (define config (cast (ptr-add (malloc _PyConfig) 0) _pointer _PyConfig-pointer)) 94 | 95 | ;; Pre Initialization 96 | 97 | (define preconfig (cast (malloc (ctype-sizeof _PyPreConfig)) 98 | _pointer _PyPreConfig*)) 99 | 100 | ; (define preconfig (make-PyPreConfig 0 0 0 0 0 0 0 0 0 0)) 101 | 102 | #;(displayln "Before PyPreConfig_InitPythonConfig") 103 | (PyPreConfig_InitPythonConfig preconfig) 104 | #;(displayln "PyPreConfig_InitPythonConfig\n") 105 | 106 | 107 | ; (set-PyPreConfig-utf8_mode! preconfig 1) ; doesn't work on GA 108 | 109 | 110 | #;(displayln "Before Py_PreInitialize") 111 | (let ([status (Py_PreInitialize preconfig)]) 112 | (unless (zero? (PyStatus_Exception status)) 113 | (Py_ExitStatusException status))) 114 | #;(displayln "After Py_PreInitialize\n") 115 | 116 | 117 | ;; Initialization 118 | 119 | (define config (cast (malloc (ctype-sizeof _PyConfig)) 120 | _pointer _PyConfig-pointer)) 121 | 122 | #;(displayln "Before InitPythonConfig") 123 | (PyConfig_InitPythonConfig config) 124 | #;(displayln "After InitPythonConfig\n") 125 | 126 | (define (decode s) (Py_DecodeLocale s #f)) 127 | 128 | (set-PyConfig-home! config (decode home)) 129 | ; (set-PyConfig-program_name! config (decode "python3.10")) 130 | (set-PyConfig-platlibdir! config (decode (string-append home "/" "lib/python3.10"))) 131 | 132 | #;(let ([pythonpath (getenv "PYTHONPATH")]) 133 | (when pythonpath 134 | (set-PyConfig-pythonpath_env! config (decode pythonpath)))) 135 | 136 | ; Leads to error: "invalid memory reference. Some debugging context lost" on GA 137 | #;(let ([status (PyConfig_Read config)]) 138 | (unless (zero? (PyStatus_Exception status)) 139 | (Py_ExitStatusException status))) 140 | 141 | 142 | #;(displayln "Before InitializeFromConfig") 143 | (let ([status (Py_InitializeFromConfig config)]) 144 | #;(displayln "Before exception check") 145 | (unless (zero? (PyStatus_Exception status)) 146 | (Py_ExitStatusException status)) 147 | #;(displayln "After InitializeFromConfig")) 148 | 149 | (initialize-main-and-builtins) 150 | 151 | (initialize-builtin-constants) ; uses `run` 152 | 153 | 154 | ; We can't run the initialization thunks here. 155 | ; The Python modules are loaded yet. 156 | #;(run-initialization-thunks)) 157 | 158 | 159 | (define (post-initialize) 160 | (run-initialization-thunks)) 161 | 162 | (define (finish-initialization) 163 | (run-initialization-thunks)) 164 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-list.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (require "structs.rkt" 6 | "python-c-api.rkt" 7 | "python-environment.rkt" 8 | "python-types.rkt") 9 | 10 | (require (for-syntax racket/base syntax/parse racket/syntax)) 11 | 12 | ;;; 13 | ;;; Lists (Python lists) 14 | ;;; 15 | 16 | (define (pylist? x) 17 | (and (obj? x) 18 | (or (equal? (obj-type-name x) "list") 19 | #;(PyList_Check (obj-the-obj x))))) 20 | 21 | (define (pylist-new len) 22 | (unless (and (integer? len) (>= len 0)) 23 | (raise-arguments-error 'pylist-new "expected a non-negative integer" "length" len)) 24 | 25 | (define l (PyList_New len)) ; new reference 26 | (obj "list" l)) 27 | 28 | (define (pylist . xs) 29 | (list->pylist xs)) 30 | 31 | (define (list->pylist xs) 32 | (unless (list? xs) 33 | (raise-arguments-error 'list->pylist "expected a list" "xs" xs)) 34 | (define n (length xs)) 35 | (define l (PyList_New n)) 36 | (for ([x (in-list xs)] [i (in-range n)]) 37 | (define v (racket->python x)) 38 | (Py_IncRef v) ; since PyList_SetItem steals reference 39 | (case (PyList_SetItem l i v) 40 | [(0) (void)] ; succes 41 | [(-1) (error 'list->pylist "some error 1")] ; out of range 42 | [else (error 'list->pylist "some error 2")])) 43 | (obj "list" l)) 44 | 45 | (define (vector->pylist xs) 46 | (define who 'vector->pylist) 47 | (unless (vector? xs) 48 | (raise-arguments-error who "expected a vector" "xs" xs)) 49 | (define n (vector-length xs)) 50 | (define l (PyList_New n)) 51 | (for ([x (in-vector xs)] [i (in-range n)]) 52 | (define v (racket->python x)) 53 | (Py_IncRef v) ; since PyList_SetItem steals reference 54 | (case (PyList_SetItem l i v) 55 | [(0) (void)] ; succes 56 | [(-1) (error who "some error 1")] ; out of range 57 | [else (error who "some error 2")])) 58 | (obj "list" l)) 59 | 60 | 61 | (define (pylist-length x) 62 | (unless (pylist? x) 63 | (raise-arguments-error 'pylist-size "expected a pylist" "pylist" x)) 64 | 65 | (define o (obj-the-obj x)) 66 | (PyList_Size o)) 67 | 68 | (define pylist-size pylist-length) 69 | 70 | 71 | (define (pylist-ref pylist index) 72 | (define who 'pylist-ref) 73 | (unless (pylist? pylist) 74 | (raise-arguments-error who "expected a pylist" "pylist" pylist "index" index)) 75 | (unless (and (integer? index) (>= index 0)) 76 | (raise-arguments-error who 77 | "expected a non-negative integer as index" "pylist" pylist "index" index)) 78 | 79 | (define l (obj-the-obj pylist)) 80 | (define v (PyList_GetItem l index)) 81 | (when (eqv? v #f) (PyErr_Clear)) 82 | (and v (python->racket v))) 83 | 84 | (define (pyfirst pylist) (pylist-ref pylist 0)) 85 | (define (pysecond pylist) (pylist-ref pylist 1)) 86 | 87 | (define pylist-get-item pylist-ref) 88 | 89 | (define (pylist->list pylist) 90 | (unless (pylist? pylist) 91 | (raise-arguments-error 'pylist->list "expected a pylist" "pylist" pylist)) 92 | 93 | (define l (obj-the-obj pylist)) 94 | (define n (PyList_Size l)) 95 | (for/list ([i (in-range n)]) 96 | (define v (PyList_GetItem l i)) 97 | (when (eqv? v #f) (PyErr_Clear)) 98 | (and v (pr v)))) 99 | 100 | (define (pylist->vector pylist) 101 | (define who 'pylist->vector) 102 | (unless (pylist? pylist) 103 | (raise-arguments-error who "expected a pylist" "pylist" pylist)) 104 | 105 | (define l (obj-the-obj pylist)) 106 | (define n (PyList_Size l)) 107 | (for/vector ([i (in-range n)]) 108 | (define v (PyList_GetItem l i)) 109 | (when (eqv? v #f) (PyErr_Clear)) 110 | (and v (pr v)))) 111 | 112 | 113 | (define (pylist-get-slice pylist low-index high-index) 114 | ; returns new pylist 115 | (unless (pylist? pylist) 116 | (raise-arguments-error 'pylist-get-slice 117 | "expected a pylist" "pylist" pylist "low" low-index "high" high-index)) 118 | (unless (and (integer? low-index) (>= low-index 0)) 119 | (raise-arguments-error 'pylist-get-slice "expected a non-negative integer as the low index" 120 | "pylist" pylist "low" low-index "high" high-index)) 121 | (unless (and (integer? high-index) (>= high-index 0)) 122 | (raise-arguments-error 'pylist-get-slice "expected a non-negative integer as the high index" 123 | "pylist" pylist "low" low-index "high" high-index)) 124 | 125 | (define l (obj-the-obj pylist)) 126 | (define v (PyList_GetSlice l low-index high-index)) 127 | (when (eqv? v #f) (PyErr_Clear)) 128 | (and v (obj "list" v))) 129 | 130 | (define (pylist-set-slice! pylist low-index high-index item-pylist) 131 | (define who 'pylist-set-slice!) 132 | (unless (pylist? pylist) 133 | (raise-arguments-error who "expected a pylist" 134 | "pylist" pylist "low" low-index "high" high-index "item-pylist" item-pylist)) 135 | (unless (and (integer? low-index) (>= low-index 0)) 136 | (raise-arguments-error who "expected a non-negative integer as the low index" 137 | "pylist" pylist "low" low-index "high" high-index "item-pylist" item-pylist)) 138 | (unless (and (integer? high-index) (>= high-index 0)) 139 | (raise-arguments-error who "expected a non-negative integer as the high index" 140 | "pylist" pylist "low" low-index "high" high-index "item-pylist" item-pylist)) 141 | (unless (pylist? item-pylist) 142 | (raise-arguments-error who "expected a pylist" 143 | "pylist" pylist "low" low-index "high" high-index "item-pylist" item-pylist)) 144 | 145 | (define l (obj-the-obj pylist)) 146 | (define i (obj-the-obj item-pylist)) 147 | (define v (PyList_SetSlice l low-index high-index i)) 148 | (when (eqv? v #f) (PyErr_Clear)) 149 | (void)) 150 | 151 | 152 | (define (pylist-set-item! pylist index value) 153 | (unless (pylist? pylist) 154 | (raise-arguments-error 'pylist-set-item! "expected a pylist" 155 | "pylist" pylist "index" index "value" value)) 156 | (unless (and (integer? index) (>= index 0)) 157 | (raise-arguments-error 'pylist-set-item! 158 | "expected a non-negative integer as index" 159 | "pylist" pylist "index" index "value" value)) 160 | 161 | (define l (obj-the-obj pylist)) 162 | (define v (racket->python value)) 163 | (Py_IncRef v) ; since PyList_SetItem steals reference 164 | (case (PyList_SetItem l index v) 165 | [(0) (void)] ; succes 166 | [(-1) (raise-range-error 'pylist-set-item! 167 | "pylist" 168 | "" 169 | index 170 | pylist 171 | 0 172 | (max 0 (- (pylist-size pylist) 1)))] 173 | [else (error 'pylist-set-item! "some error")])) 174 | 175 | (define (pylist-insert! pylist index value) 176 | (unless (pylist? pylist) 177 | (raise-arguments-error 'pylist-insert! "expected a pylist" 178 | "pylist" pylist "index" index "value" value)) 179 | (unless (and (integer? index) (>= index 0)) 180 | (raise-arguments-error 'pylist-insert! 181 | "expected a non-negative integer as index" 182 | "pylist" pylist "index" index "value" value)) 183 | 184 | (define l (obj-the-obj pylist)) 185 | (define v (racket->python value)) 186 | 187 | (case (PyList_Insert l index v) 188 | [(0) (void)] ; succes 189 | [(-1) (raise-range-error 'pylist-insert! 190 | "pylist" 191 | "" 192 | index 193 | pylist 194 | 0 195 | (max 0 (- (pylist-size pylist) 1)))] 196 | [else (error 'pylist-insert! "some error")])) 197 | 198 | (define (pylist-append-item! pylist item) 199 | (unless (pylist? pylist) 200 | (raise-arguments-error 'pylist-append-item! "expected a pylist" "pylist" pylist "item" item)) 201 | 202 | (define ol (obj-the-obj pylist)) 203 | (define oi (racket->python item)) 204 | (case (PyList_Append ol oi) 205 | [(0) (void)] 206 | [else (PyErr_Clear) 207 | (error 'pylist-append-item! "an error occurred")])) 208 | 209 | (define (pylist-reverse! pylist) 210 | (unless (pylist? pylist) 211 | (raise-arguments-error 'pylist-reverse! "expected a pylist" "pylist" pylist)) 212 | 213 | (define ol (obj-the-obj pylist)) 214 | (case (PyList_Reverse ol) 215 | [(0) (void)] 216 | [else (PyErr_Clear) 217 | (error 'pylist-reverse! "an error occurred")])) 218 | 219 | (define (pylist-sort! pylist) 220 | (unless (pylist? pylist) 221 | (raise-arguments-error 'pylist-sort! "expected a pylist" "pylist" pylist)) 222 | 223 | (define ol (obj-the-obj pylist)) 224 | (case (PyList_Sort ol) 225 | [(0) (void)] 226 | [else (PyErr_Clear) 227 | (error 'pylist-sort! "an error occurred")])) 228 | 229 | (define (pylist->pytuple pylist) 230 | (define who 'pylist->pytuple) 231 | (unless (pylist? pylist) 232 | (raise-arguments-error who "expected a pylist" "pylist" pylist)) 233 | 234 | (define ol (obj-the-obj pylist)) 235 | (define t (PyList_AsTuple ol)) 236 | (case t 237 | [(#f) (PyErr_Clear) 238 | (error who "an error occurred")] 239 | [else (obj "tuple" t)])) 240 | 241 | 242 | (define (in-pylist pylist) 243 | (define n (pylist-size pylist)) 244 | (make-do-sequence 245 | (λ () (values (λ (pos) (pylist-get-item pylist pos)) 246 | #f 247 | add1 248 | 0 249 | (λ (pos) (< pos n)) 250 | #f 251 | #f)))) 252 | 253 | (define pylist-set! pylist-set-item!) 254 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-module.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide module? 4 | module-name 5 | module-pydict 6 | module-pydict-as-hash 7 | module-hash-ref 8 | module-submodules 9 | module-functions 10 | module-functions-with-signature) 11 | 12 | (require "structs.rkt" 13 | "python-c-api.rkt" 14 | "python-environment.rkt" 15 | "python-types.rkt" 16 | "python-dict.rkt" 17 | "python-list.rkt" 18 | "python-functions.rkt") 19 | (require (for-syntax racket/base syntax/parse racket/syntax)) 20 | 21 | 22 | (define (module? x) 23 | (and (obj? x) 24 | (member (obj-type-name x) '("module" "_automodule")) 25 | #t)) 26 | 27 | (define (module-name x) 28 | ; get the name of the module as a string 29 | (cond 30 | [(and (module? x) 31 | (PyModule_GetNameObject (obj-the-obj x))) 32 | => python->racket] 33 | [else #f])) 34 | 35 | (define (module-pydict x) 36 | ; get the `dict` (namespace) of the module 37 | (cond 38 | [(and (module? x) 39 | (PyModule_GetDict (obj-the-obj x))) 40 | => (λ (d) (obj "dict" d))] 41 | [else #f])) 42 | 43 | (define (module-pydict-as-hash x) 44 | ; get the `dict` (namespace) of the module 45 | (cond 46 | [(and (module? x) 47 | (PyModule_GetDict (obj-the-obj x))) 48 | => python->racket] 49 | [else #f])) 50 | 51 | 52 | (define (module-hash-ref module-ht id [failure-result #f]) 53 | (define str (or (and (string? id) id) 54 | (and (symbol? id) (symbol->string id)) 55 | (error 'module-hash-ref "expected a string or symbol"))) 56 | (hash-ref module-ht str failure-result)) 57 | 58 | (define (module-functions x) 59 | (define d (module-pydict x)) 60 | (define ks (pydict-keys d)) 61 | (define vs (pydict-values d)) 62 | (for/list ([k (in-pylist ks)] 63 | [v (in-pylist vs)] 64 | #:when (is-function? v)) 65 | v)) 66 | 67 | (define (module-submodules x) 68 | (define d (module-pydict x)) 69 | (define ks (pydict-keys d)) 70 | (define vs (pydict-values d)) 71 | (for/list ([k (in-pylist ks)] 72 | [v (in-pylist vs)] 73 | #:when (is-module? v)) 74 | v)) 75 | 76 | (define (module-functions-with-signature x) 77 | (define d (module-pydict x)) 78 | (define ks (pydict-keys d)) 79 | (define vs (pydict-values d)) 80 | (for/list ([k (in-pylist ks)] 81 | [v (in-pylist vs)] 82 | #:when (and (is-function? v) 83 | (get-signature v))) 84 | v)) 85 | 86 | 87 | 88 | (define-syntax (define-functions stx) 89 | (syntax-parse stx 90 | [(_ qualifier:id id:id ...) 91 | (define qualifier-str (symbol->string (syntax-e #'qualifier))) 92 | (define qualified-names (for/list ([id (syntax->list #'(id ...))]) 93 | (format-id id (string-append qualifier-str ".~a") id))) 94 | (with-syntax ([(qualified-name ...) qualified-names]) 95 | (syntax/loc stx 96 | (define-delayed 97 | (define qualified-name 98 | (begin #;(displayln 'qualified-name) 99 | (get-fun 'qualified-name))) 100 | ...)))])) 101 | 102 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-more-builtins.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require "structs.rkt" 4 | "python-c-api.rkt" 5 | "python-delayed.rkt" 6 | "python-define-delayed.rkt" 7 | "python-environment.rkt" 8 | "python-evaluation.rkt" 9 | "python-initialization.rkt" 10 | 11 | "python-types.rkt" 12 | "python-attributes.rkt" 13 | "python-builtins.rkt" 14 | "python-functions.rkt" 15 | "python-operators.rkt" 16 | 17 | racket/format 18 | racket/list 19 | racket/match) 20 | 21 | (require (for-syntax racket/base 22 | racket/syntax 23 | syntax/parse)) 24 | 25 | 26 | (define (simple-proc name qualified-name 27 | positional-parameters 28 | keyword-parameters 29 | #:object-type-str [object-type-str "fun"] 30 | #:positional-excess [positional-excess #f] 31 | #:keyword-excess [keyword-excess #f] 32 | #:positional-types [positional-types #f] 33 | #:keyword-types [keyword-types #f] 34 | #:first-optional [first-optional #f] 35 | #:result-type [result-type #f]) 36 | (define object (obj object-type-str (get qualified-name))) 37 | (set! name (~a name)) ; allow symbol 38 | (set! positional-parameters (map ~a positional-parameters)) ; allow symbols 39 | (set! keyword-parameters (map ~a keyword-parameters)) ; allow symbols 40 | (when positional-excess 41 | (set! positional-excess (~a positional-excess))) ; allow symbol 42 | (when keyword-excess 43 | (set! keyword-excess (~a keyword-excess))) ; allow symbol 44 | (unless positional-types 45 | (set! positional-types (make-list (length positional-parameters) #f))) 46 | (unless keyword-types 47 | (set! keyword-types (make-list (length keyword-parameters) #f))) 48 | (pyproc object name qualified-name 49 | positional-parameters positional-types positional-excess 50 | keyword-parameters keyword-types keyword-excess 51 | first-optional result-type)) 52 | 53 | (define (simple-builtin name qualified-name 54 | positional-parameters 55 | keyword-parameters 56 | #:positional-excess [positional-excess #f] 57 | #:keyword-excess [keyword-excess #f] 58 | #:positional-types [positional-types #f] 59 | #:keyword-types [keyword-types #f] 60 | #:first-optional [first-optional #f] 61 | #:result-type [result-type #f]) 62 | (pyproc->procedure 63 | (simple-proc name qualified-name 64 | positional-parameters 65 | keyword-parameters 66 | #:object-type-str "builtins" 67 | #:positional-excess positional-excess 68 | #:keyword-excess keyword-excess 69 | #:positional-types positional-types 70 | #:keyword-types keyword-types 71 | #:first-optional first-optional 72 | #:result-type result-type))) 73 | 74 | 75 | (define-delayed 76 | (define builtins.dir 77 | (simple-builtin 'dir 'builtins.dir 78 | '(object) '()))) 79 | #;(define dir 80 | (pyproc->procedure 81 | (pyproc (obj "fun" (get 'builtins.dir)) "dir" 'builtins.dir 82 | '("object") '(#f) #f 83 | '() #f #f 84 | #f #f))) 85 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-object.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (all-defined-out)) 3 | 4 | (require "structs.rkt") 5 | 6 | (require ffi/unsafe) 7 | 8 | (define (fast-obj-eq? x y) 9 | (ptr-equal? (obj-the-obj x) (obj-the-obj y))) 10 | 11 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-operators.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide := ref) 3 | 4 | (require "structs.rkt" 5 | "python-c-api.rkt" 6 | "python-constants.rkt" 7 | "python-types.rkt" 8 | (only-in "python-builtins.rkt" slice-obj?) 9 | racket/format) 10 | 11 | (require (for-syntax racket/base racket/syntax syntax/parse)) 12 | 13 | ;;; 14 | ;;; operator - Standard operators as functions 15 | ;;; 16 | 17 | ; The Python module `operator` export a set of functions that correspond 18 | ; to the Python operators. 19 | 20 | (define-syntax (define-binary-operator stx) 21 | (syntax-parse stx 22 | [(_define-binary-operators id:id) 23 | (with-syntax ([operator.id (format-id #'id "operator.~a" #'id)]) 24 | (syntax/loc stx 25 | (begin 26 | (provide id) 27 | (define-py operator.id (~fun ~py ~py -> ~py)) 28 | (define (id x y) (operator.id x y)))))])) 29 | 30 | (define-syntax-rule (define-binary-operators id ...) 31 | (begin (define-binary-operator id) ...)) 32 | 33 | (define-syntax (define-unary-operator stx) 34 | (syntax-parse stx 35 | [(_define-unary-operators id:id) 36 | (with-syntax ([operator.id (format-id #'id "operator.~a" #'id)]) 37 | (syntax/loc stx 38 | (begin 39 | (provide id) 40 | (define-py operator.id (~fun ~py -> ~py)) 41 | (define (id x) (operator.id x)))))])) 42 | 43 | (define-syntax-rule (define-unary-operators id ...) 44 | (begin (define-unary-operator id) ...)) 45 | 46 | 47 | ;;; Object Comparisons 48 | 49 | ; These operators can potentially return non-boolean results. 50 | (define-binary-operators lt le eq ne ge gt) 51 | 52 | ;;; Logical operators 53 | (define-unary-operators not_ truth is_ is_not) 54 | 55 | ;;; Mathematical and bitwise operators 56 | ; Skipped: abs 57 | (define-binary-operators 58 | add ; a + b 59 | floordiv ; a // b 60 | and_ ; bitwise a and b 61 | index ; convert to index using __index__() 62 | lshift ; a shifted left by b 63 | mod ; a % b 64 | mul ; a * b 65 | matmul ; a @ b 66 | or_ ; bitwise a or b 67 | pow ; a ** b , a and b numbers 68 | rshift ; a shifted right by b 69 | sub ; a - b 70 | truediv ; a/b where 2/3 is 0.66 71 | xor ; bitwise a xor b 72 | ) 73 | 74 | (define-unary-operators 75 | invert ; bitwise invert 76 | neg ; -a 77 | pos ; +a [sic] 78 | ) 79 | 80 | ;;; Sequence operators 81 | 82 | (define-binary-operators 83 | concat ; a + b for a and b sequences 84 | contains ; is b in a 85 | countOf ; number of occurences of b in a 86 | delitem ; remove value of a at index b 87 | getitem ; return the value of a at index b 88 | indexOf ; return index of the first occurence of b in a 89 | ) 90 | 91 | (define-unary-operators 92 | length_hint ; estimate length 93 | ) 94 | 95 | ; setitem ; set the value of a at index b to c 96 | (define-py operator.setitem (~fun ~py ~py ~py -> ~py)) 97 | (define (setitem a b c) (operator.setitem a b c)) 98 | 99 | ;;; Generalized attrinube and item lookup 100 | 101 | ; TODO 102 | 103 | ; operator.attrgetter(attr) 104 | ; operator.attrgetter(*attrs) 105 | 106 | ; operator.itemgetter(item) 107 | ; operator.itemgetter(*items) 108 | 109 | ; operator.methodcaller(name, /, *args, **kwargs) 110 | 111 | ; In-place Operators 112 | 113 | 114 | (define-syntax (:= stx) 115 | (syntax-parse stx 116 | [(_ x:expr [i:expr] y:expr) 117 | (syntax/loc stx 118 | (operator.setitem x i y))] 119 | [(_ x:expr [i:expr j:expr] y:expr) 120 | (syntax/loc stx 121 | (let ([t (operator.getitem x i)]) 122 | (operator.setitem t j y)))] 123 | [_ (error ':= "todo")])) 124 | 125 | 126 | (define ref 127 | ; Note: a[e ...] is equivalent to a[(e,...)] where (e,...) is a tuple. 128 | (case-lambda 129 | [(seq i) 130 | (cond 131 | [(vector? seq) 132 | (vector-ref seq i)] 133 | [(integer? i) 134 | (define o (if (obj? seq) (obj-the-obj seq) seq)) 135 | (define v (PyObject_GetItem o (integer->py-int i))) 136 | (python->racket v)] 137 | [(string? i) 138 | (define o (if (obj? seq) (obj-the-obj seq) seq)) 139 | (define v (PyObject_GetItem o (string->py-string i))) 140 | (python->racket v)] 141 | [(slice-obj? i) 142 | (operator.getitem seq i)] 143 | [(vector? i) ; a vector (tuple) of indices 144 | (operator.getitem seq i)] 145 | [else (error 'ref (~a "got: " seq " " i))])] 146 | [(seq i j) 147 | (ref seq (vector i j))] 148 | [(seq i j k) 149 | (ref seq (vector i j k))] 150 | [(seq i j k l) 151 | (ref seq (vector i j k l))] 152 | [(seq i . is) 153 | (ref seq (list->vector is))])) 154 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-slice.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (require "structs.rkt" 6 | "python-c-api.rkt" 7 | "python-environment.rkt" 8 | "python-types.rkt") 9 | 10 | (require (for-syntax racket/base syntax/parse racket/syntax)) 11 | 12 | ;;; 13 | ;;; Slice Objects 14 | ;;; 15 | 16 | (define (slice? x) 17 | (and (obj? x) (equal? (obj-type-name x) "slice"))) 18 | 19 | (define slice 20 | (let ([c integer->py-int]) 21 | (case-lambda 22 | [(stop) (obj "slice" (PySlice_New #f (c stop) #f))] 23 | [(start stop) (if start 24 | (if stop 25 | (obj "slice" (PySlice_New (c start) (c stop) #f)) 26 | (obj "slice" (PySlice_New (c start) #f #f))) 27 | (obj "slice" (PySlice_New #f (c stop) #f)))] 28 | [(start stop step) (obj "slice" (PySlice_New (c start) (c stop) (c step)))]))) 29 | 30 | (define (slice-indices x length) 31 | (unless (slice? x) (error 'slice-indices "expected a slice, got: ~a" x)) 32 | (PySlice_GetIndices (obj-the-obj x) length)) 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-string.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (provide (all-defined-out)) 4 | 5 | (require "structs.rkt" 6 | "python-c-api.rkt" 7 | "python-environment.rkt" 8 | "python-operators.rkt" 9 | "python-slice.rkt" 10 | "python-types.rkt") 11 | 12 | (require racket/format) 13 | 14 | (require (for-syntax racket/base syntax/parse racket/syntax)) 15 | 16 | ;;; 17 | ;;; Strings (Unicode Objects) 18 | ;;; 19 | 20 | (define (pystring? x) 21 | (and (obj? x) (equal? (obj-type-name x) "str"))) 22 | 23 | (define (pystring . cs) 24 | (unless (andmap char? cs) 25 | (raise-arguments-error 'pystring "expected characters as input" 26 | "cs" cs)) 27 | (string->pystring (apply string cs))) 28 | 29 | (define (string->pystring x) 30 | (unless (string? x) (error 'string->pystring "got: ~a" x)) 31 | (obj "str" (PyUnicode_FromString x))) 32 | 33 | (define (pystring->string x) 34 | (unless (pystring? x) (error 'pystring->string "got: ~a" x)) 35 | (PyUnicode_AsUTF8 (obj-the-obj x))) 36 | 37 | 38 | (define (pystring-length x) 39 | (unless (pystring? x) (error 'pystring-length "got: ~a" x)) 40 | (PyObject_Length (obj-the-obj x))) 41 | 42 | (define (pystring-ref x i) 43 | (define who 'pystring-ref) 44 | (unless (pystring? x) 45 | (error who "got: ~a" x)) 46 | (unless (<= 0 i (pystring-length x)) 47 | (raise (exn:fail:contract (~a who ": index " i " out of range for the string \"" x "\"") 48 | (current-continuation-marks)))) 49 | 50 | ; Todo: should this return a Racket character instead? 51 | (string-ref (PyUnicode_AsUTF8 (PyObject_GetItem (obj-the-obj x) (PyLong_FromLong i))) 0)) 52 | 53 | (define (subpystring x start [end #f]) 54 | (define who 'subpystring) 55 | (unless (pystring? x) (error who "got: ~a" x)) 56 | ; (getitem x (slice start end)) ; alternative 57 | (obj "str" (PyUnicode_Substring (obj-the-obj x) start (or end -1)))) 58 | 59 | #;(define (pystring-slice x slice) 60 | (define who 'pystring-slice) 61 | (unless (pystring? x) (error pystring-slice "got: ~a" x)) 62 | (getitem x slice)) 63 | 64 | (define pystring-slice 65 | (case-lambda 66 | [(x stop) (getitem x (slice stop))] 67 | [(x start stop) (getitem x (slice start stop))] 68 | [(x start stop step) (getitem x (slice start stop step))])) 69 | 70 | (define (in-pystring pystring) 71 | (let ([s (pystring->string pystring)]) 72 | (in-string s))) 73 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python-tuple.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (provide (all-defined-out)) 3 | 4 | (require "structs.rkt" 5 | "python-c-api.rkt" 6 | "python-environment.rkt" 7 | (only-in "python-builtins.rkt" builtins.tuple) 8 | "python-types.rkt") 9 | 10 | (require (for-syntax racket/base syntax/parse racket/syntax)) 11 | 12 | ;;; 13 | ;;; Tuples 14 | ;;; 15 | 16 | (define (pytuple? x) 17 | (and (obj? x) 18 | (equal? (obj-type-name x) "tuple"))) 19 | 20 | (define (pytuple-new len) 21 | (unless (and (integer? len) (>= len 0)) 22 | (raise-arguments-error 'pytuple-new "expected a non-negative integer" "length" len)) 23 | 24 | (define t (PyTuple_New len)) ; new reference 25 | (obj "tuple" t)) 26 | 27 | (define (list->pytuple xs) 28 | (unless (list? xs) 29 | (raise-arguments-error 'list->pytuple "expected a list" "xs" xs)) 30 | (define n (length xs)) 31 | (define t (PyTuple_New n)) 32 | (for ([x (in-list xs)] [i (in-range n)]) 33 | (define v (racket->python x)) 34 | (Py_IncRef v) ; since Tuple_SetItem steals reference 35 | (case (PyTuple_SetItem t i v) 36 | [(0) (void)] ; succes 37 | [(-1) (error 'list->pytuple "some error 1")] ; out of range 38 | [else (error 'list->pytuple "some error 2")])) 39 | (obj "tuple" t)) 40 | 41 | (define (pytuple . xs) 42 | (list->pytuple xs)) 43 | 44 | (define (iterable->pytuple o) 45 | (builtins.tuple o)) 46 | 47 | (define (vector->pytuple xs) 48 | (define who 'vector->pytuple) 49 | (unless (vector? xs) 50 | (raise-arguments-error who "expected a vector" "xs" xs)) 51 | (define n (vector-length xs)) 52 | (define t (PyTuple_New n)) 53 | (for ([x (in-vector xs)] [i (in-range n)]) 54 | (define v (racket->python x)) 55 | (Py_IncRef v) ; since Tuple_SetItem steals reference 56 | (case (PyTuple_SetItem t i v) 57 | [(0) (void)] ; succes 58 | [(-1) (error who "some error 1")] ; out of range 59 | [else (error who "some error 2")])) 60 | (obj "tuple" t)) 61 | 62 | 63 | (define (pytuple->vector xs) 64 | (define who 'pytuple->vector) 65 | (unless (pytuple? xs) 66 | (raise-arguments-error who "expected a pytuple" "xs" xs)) 67 | 68 | (set! xs (obj-the-obj xs)) 69 | (define n (PyTuple_Size xs)) 70 | (define v (make-vector n)) 71 | (for ([i (in-range n)]) 72 | (vector-set! v i (pr (PyTuple_GetItem xs i)))) 73 | v) 74 | 75 | (define (pytuple->list xs) 76 | (define who 'pytuple->list) 77 | (unless (pytuple? xs) 78 | (raise-arguments-error who "expected a pytuple" "xs" xs)) 79 | 80 | (set! xs (obj-the-obj xs)) 81 | (define n (PyTuple_Size xs)) 82 | (for/list ([i (in-range n)]) 83 | (pr (PyTuple_GetItem xs i)))) 84 | 85 | (define (pytuple->pylist xs) 86 | (define who 'pytuple->pylist) 87 | (unless (pytuple? xs) 88 | (raise-arguments-error who "expected a pytuple" "xs" xs)) 89 | 90 | (set! xs (obj-the-obj xs)) 91 | (define n (PyTuple_Size xs)) 92 | (define l (PyList_New n)) 93 | (for ([i (in-range n)] ) 94 | (define x (PyTuple_GetItem xs i)) 95 | (Py_IncRef x) ; since PyList_SetItem steals reference 96 | (case (PyList_SetItem l i x) 97 | [(0) (void)] ; succes 98 | [(-1) (error who "some error 1")] ; out of range 99 | [else (error who "some error 2")])) 100 | (obj "list" l)) 101 | 102 | 103 | 104 | (require (only-in racket/unsafe/ops unsafe-vector*->immutable-vector!)) 105 | 106 | (define (pytuple->immutable-vector xs) 107 | (define who 'pytuple->immutable-vector) 108 | (unless (pytuple? xs) 109 | (raise-arguments-error who "expected a pytuple" "xs" xs)) 110 | 111 | (set! xs (obj-the-obj xs)) 112 | (define n (PyTuple_Size xs)) 113 | (define v (make-vector n)) 114 | (for ([i (in-range n)]) 115 | (vector-set! v i (pr (PyTuple_GetItem xs i)))) 116 | (unsafe-vector*->immutable-vector! v)) 117 | 118 | 119 | (define (pytuple-length x) 120 | (unless (pytuple? x) 121 | (raise-arguments-error 'pytuple-length "expected a pytuple" "tuple" x)) 122 | 123 | (define o (obj-the-obj x)) 124 | (PyTuple_Size o)) 125 | 126 | (define pytuple-size pytuple-length) 127 | 128 | (define (pytuple-ref tuple index) ; Python: tuple.getitem(index) 129 | (unless (pytuple? tuple) 130 | (raise-arguments-error 'pytuple-ref "expected a pytuple" "tuple" tuple "index" index)) 131 | (unless (and (integer? index) (>= index 0)) 132 | (raise-arguments-error 'pytuple-ref 133 | "expected a non-negative integer as index" "tuple" tuple "index" index)) 134 | 135 | (define t (obj-the-obj tuple)) 136 | (define v (PyTuple_GetItem t index)) 137 | (when (eqv? v #f) (PyErr_Clear)) 138 | (and v (python->racket v))) 139 | 140 | (define (pytuple-get-slice tuple low-index high-index) 141 | ; returns new tuple 142 | (unless (pytuple? tuple) 143 | (raise-arguments-error 'tuple-get-slice 144 | "expected a pytuple" "tuple" tuple "low" low-index "high" high-index)) 145 | (unless (and (integer? low-index) (>= low-index 0)) 146 | (raise-arguments-error 'pytuple-get-slice "expected a non-negative integer as the low index" 147 | "tuple" tuple "low" low-index "high" high-index)) 148 | (unless (and (integer? high-index) (>= high-index 0)) 149 | (raise-arguments-error 'pytuple-get-slice "expected a non-negative integer as the high index" 150 | "tuple" tuple "low" low-index "high" high-index)) 151 | 152 | (define t (obj-the-obj tuple)) 153 | (define v (PyTuple_GetSlice t low-index high-index)) 154 | (when (eqv? v #f) (PyErr_Clear)) 155 | (and v (obj "tuple" v))) 156 | 157 | (define (pytuple-set-item! tuple index value) 158 | (unless (pytuple? tuple) 159 | (raise-arguments-error 'pytuple-set-item! "expected a tuple" 160 | "tuple" tuple "index" index "value" value)) 161 | (unless (and (integer? index) (>= index 0)) 162 | (raise-arguments-error 'pytuple-set-item! 163 | "expected a non-negative integer as index" 164 | "tuple" tuple "index" index "value" value)) 165 | 166 | (define t (obj-the-obj tuple)) 167 | (define v (racket->python value)) 168 | (Py_IncRef v) ; since Tuple_SetItem steals reference 169 | (case (PyTuple_SetItem t index v) 170 | [(0) (void)] ; succes 171 | [(-1) (raise-range-error 'pytuple-set-item! 172 | "tuple" 173 | "" 174 | index 175 | tuple 176 | 0 177 | (max 0 (- (pytuple-size tuple) 1)))] 178 | [else (error 'pytuple-set-item! "some error")])) 179 | 180 | 181 | (define (in-pytuple tuple) 182 | (define n (pytuple-size tuple)) 183 | (make-do-sequence 184 | (λ () (values (λ (pos) (pytuple-ref tuple pos)) 185 | #f 186 | add1 187 | 0 188 | (λ (pos) (< pos n)) 189 | #f 190 | #f)))) 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/python.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "structs.rkt" 3 | "python-attributes.rkt" 4 | "python-builtins.rkt" 5 | "python-bytes.rkt" 6 | "python-c-api.rkt" 7 | "python-define-delayed.rkt" 8 | "python-dict.rkt" 9 | "python-environment.rkt" 10 | "python-evaluation.rkt" 11 | "python-functions.rkt" 12 | "python-generator.rkt" 13 | "python-initialization.rkt" 14 | "python-import.rkt" 15 | "python-list.rkt" 16 | "python-module.rkt" 17 | "python-more-builtins.rkt" 18 | "python-operators.rkt" 19 | "python-slice.rkt" 20 | "python-string.rkt" 21 | "python-tuple.rkt" 22 | "python-types.rkt") 23 | 24 | (provide (except-out 25 | (all-from-out 26 | "structs.rkt" 27 | "python-attributes.rkt" 28 | "python-builtins.rkt" 29 | "python-bytes.rkt" 30 | "python-c-api.rkt" 31 | "python-define-delayed.rkt" 32 | "python-dict.rkt" 33 | "python-environment.rkt" 34 | "python-evaluation.rkt" 35 | "python-functions.rkt" 36 | "python-generator.rkt" 37 | "python-initialization.rkt" 38 | "python-import.rkt" 39 | "python-list.rkt" 40 | "python-module.rkt" 41 | "python-more-builtins.rkt" 42 | "python-operators.rkt" 43 | "python-slice.rkt" 44 | "python-string.rkt" 45 | "python-tuple.rkt" 46 | "python-types.rkt") 47 | ; the values are wrapped in an obj struct below 48 | builtins main 49 | 50 | ; The procedures run and run* return cpointers. 51 | ; Automatic `pr` conversion is provided below 52 | run run*)) 53 | 54 | (require "python-delayed.rkt") 55 | 56 | ;; Modules: builtins, main 57 | 58 | 59 | (define obj-builtins 'uninitialized-obj-builtins) 60 | (define obj-main 'uninitialized-obj-main) 61 | 62 | (add-initialization-thunk 63 | (λ () 64 | (set! obj-builtins (obj "module" builtins)) 65 | (set! obj-main (obj "module" main)))) 66 | 67 | (provide (rename-out [obj-builtins builtins] 68 | [obj-main main])) 69 | 70 | ;; Automatic conversion: run, run* 71 | 72 | (require (for-syntax racket/base syntax/parse racket/syntax)) 73 | (require racket/format racket/string) 74 | 75 | (define py-format-exception 'uninitialized-py-format-exception) 76 | (define py-format-exception-only 'uninitialized-py-format-exception-only) 77 | (define py-format-traceback 'uninitialized-py-format-traceback) 78 | 79 | (add-initialization-thunk 80 | (λ () 81 | (set! py-format-exception (get 'traceback.format_exception)) 82 | (set! py-format-exception-only (get 'traceback.format_exception_only)) 83 | (set! py-format-traceback (get 'traceback.format_tb)))) 84 | 85 | 86 | (define-syntax (handle-python-exception stx) 87 | ; When `run` and `run*`returns NULL (represented as #f in Racket), 88 | ; it means an exception occurred on the Python side. 89 | ; We must fetch and normalize the exception, exception value and the traceback. 90 | ; Fetching the exception clears the exception flags on the Python side. 91 | ; Formatting the exception as a string is done using the Python module `traceback`. 92 | (syntax-parse stx 93 | [(_handle-python-exception qualified-name result:id) 94 | (syntax/loc stx 95 | (cond 96 | ; everything went fine 97 | [result result] 98 | ; an exception was raised 99 | [else 100 | ; fetch exception (and clear flags), then normalize the exception value 101 | (define-values (ptype pvalue ptraceback) (PyErr_Fetch)) 102 | (define-values (ntype nvalue ntraceback) (PyErr_NormalizeException ptype pvalue ptraceback)) 103 | ; format the exception so we can print it 104 | #;(define msg (let ([args (flat-vector->py-tuple (vector ntype nvalue ntraceback))]) 105 | (python->racket (PyObject_Call py-format-exception args #f)))) 106 | (define msg (let ([args (flat-vector->py-tuple (vector ntype nvalue))]) 107 | (define x (PyObject_Call py-format-exception-only args #f)) 108 | (py-list->list/pr x))) 109 | (define tb (and ntraceback 110 | (let ([args (flat-vector->py-tuple (vector ntraceback))]) 111 | (py-list->list/pr (PyObject_Call py-format-traceback args #f))))) 112 | ;; Racket error message convention: 113 | ;; ‹srcloc›: ‹name›: ‹message›; 114 | ;; ‹continued-message› ... 115 | ;; ‹field›: ‹detail› 116 | (define full-msg 117 | (~a ; "src-loc" ": " 118 | qualified-name ": " "Python exception occurred" ";\n" 119 | (string-append* (map (λ (m) (~a " " (pystring->string m))) msg)) 120 | (if tb (string-append* (map (λ (m) (~a " " m)) tb)) ""))) 121 | (raise (exn full-msg (current-continuation-marks)))]))])) 122 | 123 | (define (prrun x) 124 | (define result (run x)) 125 | (handle-python-exception 'run result) 126 | (pr result)) 127 | 128 | (define (prrun* x) 129 | (define result (run* x)) 130 | (handle-python-exception 'run* result) 131 | (void result)) 132 | 133 | 134 | (provide (rename-out [prrun run] 135 | [prrun* run*])) 136 | -------------------------------------------------------------------------------- /pyffi-lib/pyffi/structs.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require "parameters.rkt" 3 | racket/string 4 | racket/serialize) 5 | 6 | (provide (struct-out pytype) 7 | (struct-out pyprocedure) 8 | (struct-out pyproc) 9 | 10 | (struct-out obj) 11 | (struct-out callable-obj) 12 | (struct-out method-obj) 13 | (struct-out generator-obj) 14 | (struct-out asis)) 15 | 16 | (struct pytype (type racket-to-python python-to-racket)) 17 | 18 | (struct obj (type-name the-obj) 19 | #:transparent 20 | #:methods gen:custom-write 21 | [(define (write-proc obj port mode) (obj-print obj port mode))] 22 | #:property prop:serializable 23 | ; Note: The serialization doesn't serialize the Python object. 24 | ; Serialization was added to support logging of results of examples in the manual, 25 | ; but may not be needed at this point. 26 | (make-serialize-info 27 | (λ (this) (vector (obj-type-name this))) 28 | (cons 'obj-deserialize-info 'pyffi/structs) 29 | #f ; can cycle? 30 | (or (current-load-relative-directory) (current-directory))) 31 | ) 32 | 33 | (provide obj-deserialize-info) 34 | (define obj-deserialize-info 35 | (make-deserialize-info 36 | ; make 37 | (λ (type-name) 38 | (obj type-name #f)) 39 | ; cycle make 40 | (λ () 'todo))) 41 | 42 | (struct callable-obj obj (app) 43 | #:property prop:procedure (struct-field-index app)) 44 | 45 | (struct method-obj obj (app) 46 | #:property prop:procedure (struct-field-index app)) 47 | 48 | (struct generator-obj obj () 49 | #:property prop:sequence (λ(gen) ((current-pygenerator-prop:sequence) gen))) 50 | 51 | ; unsafe-struct*-ref(struct-field-index gen) 52 | 53 | (struct pyprocedure (input-types output-type keywords keyword-types optional-after) #:transparent) 54 | 55 | 56 | ; This describes the parameters of a Python function 57 | (struct pyproc (object name qualified-name 58 | positional-parameters positional-types positional-excess 59 | keyword-parameters keyword-types keyword-excess 60 | first-optional result-type) 61 | #:transparent) 62 | 63 | 64 | (define (obj-print obj port mode) 65 | ; Note: 66 | ; Called by the repr() built-in function to compute the “official” 67 | ; string representation of an object. If at all possible, this 68 | ; should look like a valid Python expression that could be used to 69 | ; recreate an object with the same value (given an appropriate 70 | ; environment). 71 | 72 | ; Called by str(object) and the built-in functions format() and 73 | ; print() to compute the “informal” or nicely printable string 74 | ; representation of an object. 75 | ; Conclusion: 76 | ; Use __repr__ for `write` and __str__ for `display`. 77 | (define repr (current-repr)) 78 | (define str (current-str)) 79 | (when mode (write-string "(obj " port)) 80 | (when (callable-obj? obj) (write-string "callable " port)) 81 | (when (method-obj? obj) (write-string "method " port)) 82 | (when (generator-obj? obj) (write-string "generator " port)) 83 | (let ([tn (obj-type-name obj)] 84 | [o (obj-the-obj obj)] 85 | [recur (case mode 86 | [(#t) write] 87 | [(#f) display] 88 | [else (lambda (p port) (print p port mode))])]) 89 | (cond 90 | [o (when mode (write tn port)) ; write 91 | (when mode (display " : " port)) ; write 92 | (define r (if mode (repr obj) (str obj))) 93 | (when (string-contains? r "\n") 94 | (newline port)) 95 | (display r port)] 96 | [else 97 | (display tn port)])) 98 | (when mode (write-string ")" port))) 99 | 100 | ; Note: Is the name of the positional excess parameter important 101 | 102 | ; Note: Keyword paramers with no default value. 103 | 104 | 105 | ; positional arguments 106 | ; keyword arguments 107 | 108 | ; When one or more parameters have the form parameter = expression, 109 | ; the function is said to have “default parameter values.” 110 | ; For a parameter with a default value, the corresponding argument 111 | ; may be omitted from a call, in which case the parameter’s default value is substituted. 112 | 113 | ; If a parameter has a default value, all following parameters up until the “*” must 114 | ; also have a default value — this is a syntactic restriction that is not expressed by the grammar. 115 | 116 | ;; Function call semantics are described in more detail in section 117 | ;; Calls. A function call always assigns values to all parameters 118 | ;; mentioned in the parameter list, either from positional arguments, 119 | ;; from keyword arguments, or from default values. 120 | 121 | ;; If the form “*identifier” is present, it is initialized to a tuple 122 | ;; receiving any excess positional parameters, defaulting to the empty 123 | ;; tuple. 124 | 125 | ;; If the form “**identifier” is present, it is initialized to a new 126 | ;; ordered mapping receiving any excess keyword arguments, defaulting to 127 | ;; a new empty mapping of the same type. 128 | 129 | ;; Parameters after “*” or “*identifier” are keyword-only parameters and 130 | ;; may only be passed by keyword arguments. 131 | 132 | ;; Parameters before “/” are positional-only parameters and may only 133 | ;; be passed by positional arguments. 134 | 135 | 136 | ; Used by the manual to serialize results from examples. 137 | (serializable-struct asis (s) 138 | #:property prop:custom-write 139 | (lambda (v p m) (display (asis-s v) p))) 140 | 141 | 142 | -------------------------------------------------------------------------------- /pyffi-tests/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define compile-omit-paths 'all) 4 | 5 | (define deps '("base")) 6 | (define build-deps '()) 7 | 8 | (define pkg-desc "Use Python from Racket - Test Suite") 9 | 10 | (define pkg-authors '(soegaard)) 11 | 12 | (define version "1.0") 13 | 14 | (define test-responsibles '((all jensaxel@soegaard.net))) 15 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-app.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi/structs 3 | pyffi/python-c-api 4 | pyffi/python-initialization 5 | pyffi/python-environment 6 | pyffi/python-evaluation 7 | pyffi/python-types 8 | pyffi/python-builtins 9 | pyffi/python-operators 10 | pyffi/python-attributes 11 | racket/format 12 | racket/match) 13 | 14 | (require (for-syntax racket/base syntax/parse)) 15 | 16 | (define (import-inspect) 17 | (define empty-from-list (PyList_New 0)) 18 | (define mod:inspect (PyImport_ImportModuleEx "inspect" globals globals empty-from-list)) 19 | (when mod:inspect 20 | (void (PyModule_AddObjectRef main "inspect" mod:inspect)))) ; 0=success 21 | 22 | ;;; 23 | ;;; Start Python and import "numpy" 24 | ;;; 25 | 26 | (initialize) ; handles `main` and `builtins` 27 | (import-inspect) 28 | ; ; import and initialize numpy before 29 | ; ; running the delayed initializers 30 | (finish-initialization) ; run delayed setters 31 | 32 | (define-py inspect.signature (~fun ~py -> ~py)) 33 | (finish-initialization) 34 | 35 | (inspect.signature (obj "pyfun" (get 'inspect.signature))) 36 | (.parameters (inspect.signature (obj "pyfun" (get 'inspect.signature)))) 37 | 38 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-attributes.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require pyffi) 4 | 5 | (initialize) 6 | (post-initialize) 7 | 8 | main 9 | main.builtins 10 | main.builtins.list 11 | (main.builtins.list "abc") 12 | 13 | ;(pretty-print (syntax->datum (expand #'builtins.list))) 14 | ;(pretty-print (syntax->datum (expand #'main.builtins.list))) 15 | 16 | builtins 17 | builtins.list 18 | (builtins.list "abc") 19 | (builtins.dir (builtins.list "abc")) 20 | ((builtins.list "abc").count "a") 21 | 22 | ((builtins.list "abc").copy .count "a") ; todo: make this work 23 | 24 | ;(len (builtins.list "abc")) ; note: no `len` method 25 | 26 | 27 | ;(hasattr main "list") 28 | ;(builtins.dir main) 29 | ;(getattr main "list") 30 | 31 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-auto-numpy.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi pyffi/numpy 3 | racket/format racket/list racket/match 4 | (for-syntax racket/base racket/syntax syntax/parse)) 5 | 6 | ;;; 7 | ;;; Start Python and import "numpy" 8 | ;;; 9 | 10 | (initialize) ; handles `main` and `builtins` 11 | (import-numpy) 12 | ; ; load the `numpy` module 13 | ; ; import and initialize numpy before 14 | ; ; running the delayed initializers 15 | (finish-initialization) ; run delayed setters 16 | 17 | 18 | 19 | (define rng (numpy.random.default_rng 12345)) 20 | rng 21 | (tell rng random -> ~py) 22 | (tell rng beta 1 ~py 1 ~py -> ~py) 23 | (numpy.random.beta 1 1) 24 | 25 | ;(get-fun-as-pyproc 'numpy.linalg.det) 26 | ;(define numpy.linalg.det (get-fun 'numpy.linalg.det)) 27 | 28 | 'foo 29 | ;(get-fun-as-pyproc 'numpy.vander) 30 | ;(define vander (get-fun 'numpy.vander)) 31 | 32 | 'bar 33 | (define x (numpy.array '[1 2 3 5])) 34 | (from (numpy.vander x)) 35 | (from (numpy.vander x #:increasing #t)) 36 | ;(from (numpy.linalg.det (numpy.vander x))) 37 | 38 | 39 | 40 | (from (numpy.stack (list (numpy.array '[1 2 3 4]) 41 | (numpy.array '[5 6 7 8])))) 42 | 43 | 44 | (for/vector ([x (numpy.histogram '[0 1 1 2 2 2 3 4 4 4 4 4 5 5 6 6 6 7 7 7 7 7 7 8 8 9 10])]) 45 | (from x)) 46 | 47 | (define det (get-fun 'numpy.linalg.det)) 48 | (from (det (numpy.array '[[1 2][3 4]]))) 49 | 50 | (displayln (repr (numpy.array '[[1 2][3 4]]))) 51 | 52 | (numpy.array '[[1 2][3 4]]) 53 | 54 | '--------------- 55 | 56 | 57 | (numpy.radians (numpy.array '[[0 90] [180 360]])) 58 | 59 | 60 | (numpy.arange 5) 61 | (numpy.arange 1 5) 62 | (numpy.arange 2 5 1) 63 | (numpy.arange 1001) 64 | 65 | 66 | 67 | ; (define numpy.absolute (get-ufunc 'numpy.absolute)) 68 | 69 | ; (numpy.absolute (numpy.array '[[-1 2] [-3 3]])) 70 | 71 | #;(struct pyproc (object name qualified-name 72 | positional-parameters positional-types positional-excess 73 | keyword-parameters keyword-types keyword-excess 74 | result-type) 75 | #:transparent) 76 | 77 | 78 | (newline)(newline) 79 | (numpy.linalg.svd (numpy.array '[[1 2] [3 4]])) 80 | 81 | ;; (define n 100000) 82 | ;; (define as (make-vector n 1.)) 83 | ;; (define bs (make-vector n 1.)) 84 | ;; (time 85 | ;; (for/sum ([a (in-vector as)] 86 | ;; [b (in-vector bs)]) 87 | ;; (* a b))) 88 | 89 | ;; (define A (numpy.ones (vector n))) 90 | ;; (define B (numpy.ones (vector n))) 91 | ;; (time (numpy.dot A B)) 92 | 93 | 94 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-builtins.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi/structs 3 | pyffi/python-c-api 4 | pyffi/python-initialization 5 | pyffi/python-environment 6 | pyffi/python-evaluation 7 | pyffi/python-types 8 | pyffi/python-constants 9 | pyffi/python-operators 10 | pyffi/python-builtins 11 | pyffi/python-list 12 | pyffi/python-string 13 | racket/format 14 | ) 15 | 16 | (require (for-syntax racket/base 17 | syntax/parse)) 18 | 19 | ;;; Start Python and load "numpy" 20 | 21 | 'before-initialize 22 | (initialize) ; handles `main` and `builtins` 23 | 'initialize-done 24 | (finish-initialization) ; run delayed setters 25 | 26 | ;(void (run* "import operator as operator")) 27 | 28 | aiter 29 | (equal? (all '(#t #t #t)) #t) 30 | (equal? (all '(#t #f #t)) #f) 31 | (equal? (any '(#f #f #f)) #f) 32 | (equal? (any '(#f #f #t)) #t) 33 | (equal? (ascii "foo\nbar") "'foo\\nbar'") 34 | (equal? (bin 11) "0b1011") 35 | (equal? (bool 1) #t) 36 | (equal? (bool #t) #t) 37 | (equal? (bool #f) #f) 38 | (equal? (repr (bytearray 3)) "bytearray(b'\\x00\\x00\\x00')") 39 | (equal? (repr (bytearray "foo" "utf-8")) "bytearray(b'foo')") 40 | (equal? (repr (bytes "foo" "utf-8")) "b'foo'") 41 | (equal? (callable 42) #f) 42 | (equal? (callable (obj "fun" (get 'builtins.any))) #t) 43 | (equal? (chr 97) "a") 44 | (equal? (repr (complex)) "0j") 45 | (equal? (repr (complex 1)) "(1+0j)") 46 | (equal? (repr (complex 1 2)) "(1+2j)") 47 | ; (delattr obj "foo") ; todo 48 | (equal? (repr (dict (hash "foo" 42 "bar" 43))) "{'foo': 42, 'bar': 43}") 49 | (equal? (getitem (dict (hash "foo" 42 "bar" 43)) "foo") 42) 50 | (equal? (divmod 11 4) #(2 3)) 51 | (equal? (repr (builtins.list (enumerate "foo"))) "[(0, 'f'), (1, 'o'), (2, 'o')]") 52 | ; (builtins.eval "1") ; todo 53 | ;(builtins.filter (λ (x) #t) "foo") 54 | (equal? (builtins.float "12.34") 12.34) 55 | (equal? (builtins.float) 0.0) 56 | ; (equal? (builtins.format #(3 5) "X: {0[0]}; Y: {0[1]}") "X: 3; Y: 5") 57 | (equal? (builtins.format 17 "x") "11") ; x means hex format 58 | (equal? (getattr (dict (hash "foo" 42)) "foo" "missing") "missing") 59 | ; (builtins.globals) ; todo? 60 | (equal? (hasattr (dict (hash "foo" 42)) "bar") #f) 61 | 62 | ; 'Problem-large-ints ; Is the problem py-int->integer ? 63 | ; try hashing a string 64 | (builtins.hash 42.3) 65 | (equal? (builtins.hash 42.3) 691752902764101674) 66 | 67 | (equal? (hex 17) "0x11") 68 | (let ([d (dict (hash))]) (equal? (builtins.id d) (builtins.id d))) 69 | (equal? (builtins.int) 0) 70 | (equal? (builtins.int "12") 12) 71 | ; isinstance 72 | ; issubclass 73 | (equal? (let ([i (iter "foo")]) 74 | (map pystring->string (pylist->list (builtins.list i)))) 75 | '("f" "o" "o")) 76 | (equal? (len (builtins.list "foo")) 3) 77 | ; (builtins.locals) ; todo 78 | (equal? (let ([i (iter "foo")]) (list (next i) (next i) (next i) (next i "done"))) 79 | '("f" "o" "o" "done")) 80 | (equal? (repr object) "") 81 | (equal? (ord #\a) 97) 82 | (equal? (builtins.pow 2 3) 8) 83 | (equal? (builtins.pow 2 4 5) 1) ; 2⁴ mod 5 = 16 mod 5 = 1 84 | (equal? (pylist->list (builtins.list (builtins.range 3))) '(0 1 2)) 85 | (equal? (pylist->list (builtins.list (reversed (builtins.range 3)))) '(2 1 0)) 86 | (equal? (builtins.round 2.675 2) 2.67) 87 | (equal? (builtins.round 0.5) 0) 88 | (equal? (sorted (builtins.list (builtins.set "foo"))) '("f" "o")) 89 | (equal? (sorted "foo") '("f" "o" "o")) 90 | (equal? (str 42) "42") 91 | (equal? (str (dict (hash "foo" 42 "bar" 43))) "{'foo': 42, 'bar': 43}") 92 | (equal? (sum '(1 2 3)) 6) 93 | (equal? (builtins.tuple "foo") '#("f" "o" "o")) 94 | 95 | ;(repr (dir (dict (hash "foo" 42 "bar" 43)))) 96 | ;; (repr 42) 97 | ;; len 98 | ;; builtins.slice 99 | 100 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-dict.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | (require (for-syntax racket/base syntax/parse racket/syntax)) 4 | 5 | (initialize) 6 | (finish-initialization) 7 | 8 | 9 | #;(let ([d (pydict-new)]) 10 | (pydict-set! d "foo" "f") 11 | (pydict-set! d "bar" 42) 12 | (pydict-delete! d "foo") 13 | d 14 | #;(list (dict-keys d) (dict-values d)) 15 | #;(equal? (list (dict-keys d) (dict-values d)) 16 | (list '("bar") '(42)))) 17 | 18 | 19 | #;(let ([d (pydict-new)]) 20 | (pydict-set! d "foo" "f") 21 | (pydict-set! d "bar" 42) 22 | (list (pydict-ref d "bar") 23 | (pydict-ref d "foo") 24 | (pydict-ref d "baz"))) 25 | 26 | #;(let ([d1 (pydict-new)] [d2 (pydict-new)]) 27 | (pydict-set! d1 "foo" "f") 28 | (pydict-set! d1 "bar" 42) 29 | (pydict-set! d2 "foo" "g") 30 | (pydict-set! d2 "baz" 43) 31 | (pydict-merge! d1 d2) 32 | d1) 33 | 34 | (pydict->hash (hash->pydict (hash "a" 1 "b" 2))) 35 | 36 | (pydict "a" 1 "b" 2) 37 | 38 | (pydict->key/values (pydict "a" 1 "b" 2)) 39 | 40 | (for/list ([(k v) (in-pydict (pydict "a" 1 "b" 2))]) 41 | (vector k v)) 42 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-exceptions.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require pyffi) 4 | (initialize) 5 | (finish-initialization) 6 | 7 | (void (run* "def provoke0(baz=1): return 2/0")) 8 | (define provoke0 (get-fun 'provoke0)) 9 | (provoke0) 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-exn.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (define foo 4 | (let () 5 | (define proc (λ () (raise (exn "hello" (current-continuation-marks))))) 6 | (procedure-rename proc 'bar))) 7 | 8 | (displayln (foo)) 9 | 10 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-function.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi/structs 3 | pyffi/python-c-api 4 | pyffi/python-initialization 5 | pyffi/python-environment 6 | pyffi/python-evaluation 7 | pyffi/python-types 8 | ;"python-constants 9 | pyffi/python-builtins 10 | pyffi/python-operators 11 | pyffi/python-attributes 12 | pyffi/python-functions 13 | racket/format 14 | racket/list 15 | racket/match) 16 | 17 | (require (for-syntax racket/base syntax/parse)) 18 | 19 | 20 | ;;; 21 | ;;; Start Python and import "numpy" 22 | ;;; 23 | 24 | (initialize) ; handles `main` and `builtins` 25 | ;(import-inspect) 26 | ;(require "numpy.rkt") 27 | ;(initialize-numpy) ; load the `numpy` module 28 | ; ; import and initialize numpy before 29 | ; ; running the delayed initializers 30 | (finish-initialization) ; run delayed setters 31 | 32 | 33 | 'h1 34 | ((get-fun 'inspect.ismodule) 35 | 42) 36 | 37 | 'h2 38 | ((get-fun 'operator.add) 39 | 11 22) 40 | 41 | ; Test only positional arguments from 0,1,2 to many 42 | 'foo0 43 | (void (run* "def foo0(): return 1")) 44 | ((get-fun 'foo0)) 45 | 46 | 'foo1 47 | (void (run* "def foo1(x): return x+10")) 48 | ((get-fun 'foo1) 49 | 2) 50 | 51 | 'foo2 52 | (void (run* "def foo2(x,y): return x+y")) 53 | ((get-fun 'foo2) 54 | 11 33) 55 | 56 | 'foo3 57 | (void (run* "def foo3(x,y,z): return x+y+z")) 58 | ((get-fun 'foo3) 59 | 100 20 3) 60 | 61 | ;; Test positional excess 62 | 'bar0 63 | (void (run* "def bar0(*xs): return sum(xs)")) 64 | ((get-fun 'bar0) 65 | 100 20 3) 66 | 67 | 'bar1 68 | (void (run* "def bar1(x0,*xs): return x0+sum(xs)")) 69 | ((get-fun 'bar1) 70 | 100 20 4) 71 | 72 | 'bar2 73 | (void (run* "def bar2(x0,x1,*xs): return x0+x1+sum(xs)")) 74 | ((get-fun 'bar2) 75 | 100 20 5) 76 | 77 | 'bar3 78 | (void (run* "def bar3(x0,x1,x2,*xs): return x0+x1+x2+sum(xs)")) 79 | ((get-fun 'bar3) 80 | 1000 200 30 4) 81 | 82 | 'baz0 83 | (void (run* "def baz0(k=0): return k")) 84 | (get-fun 'baz0) 85 | ((get-fun 'baz0) 86 | #:k 10) 87 | 88 | 'baz1 89 | (void (run* "def baz1(a,k=0): return a+k")) 90 | (get-fun 'baz1) 91 | ((get-fun 'baz1) 92 | 10 #:k 2) 93 | 94 | 'baz2 95 | (void (run* "def baz2(a,b,k=0,l=1): return a+b+k+l")) 96 | (get-fun 'baz2) 97 | ((get-fun 'baz2) 98 | 1000 200 #:k 30 #:l 4) 99 | 100 | 'baz3 101 | (void (run* "def baz3(a,b,c,k=0,l=1,m=2): return a+b+c+k+l+m")) 102 | (get-fun 'baz3) 103 | ((get-fun 'baz3) 104 | 100000 20000 3000 #:k 400 #:l 50 #:m 6) 105 | 106 | 107 | (get-fun 'baz3) 108 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-generator.rkt: -------------------------------------------------------------------------------- 1 | #lang at-exp racket 2 | 3 | (require pyffi racket/sequence) 4 | 5 | (initialize) 6 | (post-initialize) 7 | 8 | (run* @~a{def f(): 9 | x=0 10 | while 1: 11 | x=x+1 12 | yield x}) 13 | 14 | (run* @~a{def g(): 15 | yield 41 16 | yield 42 17 | yield 43 18 | return 0}) 19 | 20 | 21 | 22 | (for/list ([_ 5] [x (main.f)]) x) 23 | (for/list ([_ 5] [x (main.g)]) x) 24 | 25 | (let ([g (main.f)]) 26 | (list (next g) 27 | (next g) 28 | (next g) 29 | (next g) 30 | (next g))) 31 | 32 | (run* @~a{def f(): 33 | x=0 34 | while 1: 35 | x=x+1 36 | yield x}) 37 | 38 | (let ([g (main.f)]) 39 | (for ([_ 5] [x (in-pygenerator g)]) 40 | x)) 41 | 42 | (let ([g (main.f)]) 43 | (for ([_ 5] [x g]) 44 | x)) 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-has-module.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi/structs 3 | pyffi/python-c-api 4 | pyffi/python-initialization 5 | pyffi/python-environment 6 | pyffi/python-evaluation 7 | pyffi/python-types 8 | racket/format 9 | ) 10 | 11 | (require (for-syntax racket/base 12 | syntax/parse)) 13 | 14 | ;;; Start Python and load "numpy" 15 | 16 | (initialize) ; handles `main` and `builtins` 17 | ;(initialize-numpy) ; load the `numpy` module 18 | (finish-initialization) ; run delayed setters 19 | 20 | (id-bound? 'operator) 21 | (void (run* "import operator as operator")) 22 | (id-bound? 'operator) 23 | 24 | (id-bound? 'builtins) 25 | 26 | ;(void (run* "import operator as operator")) 27 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-list.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | (require (for-syntax racket/base syntax/parse racket/syntax)) 4 | 5 | (initialize) 6 | (finish-initialization) 7 | 8 | (let ([l (pylist-new 2)]) 9 | (pylist-size l)) 10 | 11 | (let ([l (pylist-new 2)]) 12 | (pylist-set-item! l 0 "foo") 13 | (pylist-set-item! l 1 "bar") 14 | ; (pylist-set-item! l 2 "baz") 15 | l) 16 | 17 | (let ([l (pylist-new 2)]) 18 | (pylist-set-item! l 0 "foo") 19 | (pylist-set-item! l 1 "bar") 20 | (equal? (list (pylist-get-item l 0) (pylist-get-item l 1)) 21 | '("foo" "bar"))) 22 | 23 | (list->pylist '(1 2 3 "foo")) 24 | (vector->pylist #(1 2 3 "foo")) 25 | 26 | 27 | (pylist-get-slice (vector->pylist #(10 11 12 13 14 15 16 17 18 19)) 2 5) 28 | (let ([l (vector->pylist #(10 11 12 13 14 15 16 17 18 19))]) 29 | (pylist-set-slice! l 2 5 (pylist 2 3 4)) 30 | l) 31 | 32 | (for/list ([x (in-pylist (vector->pylist #(10 11 12)))]) 33 | x) 34 | 35 | 36 | (let ([l (pylist-new 0)]) 37 | (pylist-append-item! l "a") 38 | (pylist-append-item! l "b") 39 | (pylist-append-item! l "c") 40 | l) 41 | 42 | (let ([l (pylist-new 0)]) 43 | (pylist-insert! l 0 "a") 44 | (pylist-insert! l 0 "b") 45 | (pylist-insert! l 0 "c") 46 | l) 47 | 48 | (let ([l (list->pylist (list 1 2 3))]) 49 | (pylist-reverse! l) 50 | l) 51 | 52 | (let ([l (list->pylist (list 1 3 2))]) 53 | (pylist-sort! l) 54 | l) 55 | 56 | (pylist->pytuple (pylist 1 2 3)) 57 | 58 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-module.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | (require (for-syntax racket/base syntax/parse racket/syntax)) 4 | 5 | (initialize) 6 | (finish-initialization) 7 | 8 | 9 | (import-into-python 'pygments) 10 | (import-into-python 'pygments.lexers) 11 | (import-into-python 'pygments.formatters) 12 | 13 | (define pygments (python->racket (get 'pygments))) 14 | (define pygments.lexers (python->racket (get 'pygments.lexers))) 15 | (define pygments.formatters (python->racket (get 'pygments.formatters))) 16 | 17 | 'module? 18 | (and (module? pygments) (module? pygments.lexers) (module? pygments.formatters)) 19 | 20 | 21 | 'module-name 22 | (and (equal? (module-name pygments) "pygments") 23 | (equal? (module-name pygments.lexers) "pygments.lexers") 24 | (equal? (module-name pygments.formatters) "pygments.formatters")) 25 | 26 | 27 | (define pygments-hash (module-pydict-as-hash pygments)) 28 | (define pygments.lexers-hash (module-pydict-as-hash pygments.lexers)) 29 | (define pygments.formatters-hash (module-pydict-as-hash pygments.formatters)) 30 | 31 | 32 | 33 | (module-name pygments.lexers) 34 | 35 | ;; (dict-keys (module-dict pygments)) 36 | ;; (void (dict-values (module-dict pygments))) 37 | ;; (dict-size (module-dict pygments)) 38 | ;; (dict-new) 39 | ;; (dict-proxy-new (dict-new)) 40 | 41 | (define d (module-pydict pygments)) 42 | ; (dict-values d) 43 | 44 | (module-functions-with-signature pygments) 45 | ;(module-functions pygments.lexers) 46 | ;(module-functions pygments.formatters) 47 | 48 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-numpy-game-of-life.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi pyffi/numpy) 3 | 4 | ;;; Conway's Game of Life 5 | 6 | (initialize) ; handles `main` and `builtins` 7 | (import-numpy) 8 | ; ; load the `numpy` module 9 | ; ; import and initialize numpy before 10 | ; ; running the delayed initializers 11 | (finish-initialization) ; run delayed setters 12 | 13 | 14 | 15 | (define Z (numpy.array '[[0 0 0 0 0 0] 16 | [0 0 0 1 0 0] 17 | [0 1 0 1 0 0] 18 | [0 0 1 1 0 0] 19 | [0 0 0 0 0 0] 20 | [0 0 0 0 0 0]])) 21 | 22 | ;; The matrix has a border consisting of zeros. 23 | ;; This makes it easier to apply the rules without worrying about special case. 24 | ;; We do not want to show the border however, so we use `show` to display the 25 | ;; the inner parts of our matrix. 26 | 27 | (define (show A) 28 | ; remove outer border 29 | ; for each row, display it 30 | (for ([i (len A)]) 31 | (displayln (numpy.ndarray.tolist (ref A i)))) 32 | (newline)) 33 | 34 | 35 | 'Z 36 | (show Z) 37 | 'Z.T 38 | (show (.T Z)) 39 | 'Z 40 | (show Z) 41 | 42 | 43 | ;; def compute_neigbours(Z): 44 | ;; shape = len(Z), len(Z[0]) 45 | ;; N = [[0,]*(shape[0]) for i in range(shape[1])] 46 | ;; for x in range(1,shape[0]-1): 47 | ;; for y in range(1,shape[1]-1): 48 | ;; N[x][y] = Z[x-1][y-1]+Z[x][y-1]+Z[x+1][y-1] \ 49 | ;; + Z[x-1][y] +Z[x+1][y] \ 50 | ;; + Z[x-1][y+1]+Z[x][y+1]+Z[x+1][y+1] 51 | ;; return N 52 | 53 | (define (compute-neighbours Z) 54 | ;; (displayln (list (list 'len len) 55 | ;; (list 'numpy.array numpy.array))) 56 | 57 | (define shape (vector (len Z) (len (ref Z 0)))) 58 | (define N (numpy.array 59 | (for/list ([i (ref shape 1)]) 60 | (mul '(0) (ref shape 0))))) 61 | (for*/list ([x (in-range 1 (- (ref shape 0) 1))] 62 | [y (in-range 1 (- (ref shape 1) 1))]) 63 | (define-values (x- x+) (values (- x 1) (+ x 1))) 64 | (define-values (y- y+) (values (- y 1) (+ y 1))) 65 | (:= N [x y] (+ (ref Z x- y-) (ref Z x y-) (ref Z x+ y-) 66 | (ref Z x- y ) (ref Z x+ y) 67 | (ref Z x- y+) (ref Z x y+) (ref Z x+ y+)))) 68 | N) 69 | 70 | ;; ;; >>> N = np.zeros(Z.shape, dtype=int) 71 | ;; ;; >>> N[1:-1,1:-1] += (Z[ :-2, :-2] + Z[ :-2,1:-1] + Z[ :-2,2:] + 72 | ;; ;; Z[1:-1, :-2] + Z[1:-1,2:] + 73 | ;; ;; Z[2: , :-2] + Z[2: ,1:-1] + Z[2: ,2:]) 74 | ;; (define (compute-neighbours/v2 Z) 75 | ;; (define N (numpy.zeros (numpy.shape Z) #:dtype int8)) 76 | ;; N) 77 | 78 | 79 | (show (compute-neighbours Z)) 80 | 81 | ;; ;; def iterate(Z): 82 | ;; ;; N = compute_neighbours(Z) 83 | ;; ;; for x in range(1,shape[0]-1): 84 | ;; ;; for y in range(1,shape[1]-1): 85 | ;; ;; if Z[x][y] == 1 and (N[x][y] < 2 or N[x][y] > 3): 86 | ;; ;; Z[x][y] = 0 87 | ;; ;; elif Z[x][y] == 0 and N[x][y] == 3: 88 | ;; ;; Z[x][y] = 1 89 | ;; ;; return Z 90 | 91 | (define (iterate Z) 92 | (define N (compute-neighbours Z)) 93 | (define shape (vector (len Z) (len (ref Z 0)))) 94 | (for* ([x (in-range 1 (- (ref shape 0) 1))] 95 | [y (in-range 1 (- (ref shape 1) 1))]) 96 | (cond [(and (= (ref Z x y) 1) (or (< (ref N x y) 2) (> (ref N x y) 3))) 97 | (:= Z [x y] 0)] 98 | [(and (= (ref Z x y) 0) (= (ref N x y) 0)) 99 | (:= Z [x y] 1)])) 100 | Z) 101 | 102 | (show Z) 103 | (void (for ([i 4]) (iterate Z))) 104 | (show Z) 105 | 106 | ;; (show (ref Z (slice 1 3) (slice 1 3))) 107 | 108 | ;; (define B (ref Z (vector (slice 0 5) (slice 0 5)))) 109 | ;; 'B 110 | ;; (show B) 111 | ;; (len B) 112 | ;; (len (ref B 0)) 113 | 114 | ;; ; (slice 1 -1) 115 | ;; ;(integer->py-int 1) 116 | 117 | ;; (show (compute-neighbours/v2 Z)) 118 | 119 | 120 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-numpy-misc.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi pyffi/numpy 3 | racket/format racket/list racket/match 4 | (for-syntax racket/base racket/syntax syntax/parse)) 5 | 6 | ;;; 7 | ;;; Start Python and import "numpy" 8 | ;;; 9 | 10 | (initialize) ; handles `main` and `builtins` 11 | (import-numpy) 12 | ; ; load the `numpy` module 13 | ; ; import and initialize numpy before 14 | ; ; running the delayed initializers 15 | (finish-initialization) ; run delayed setters 16 | 17 | 18 | (define rng (numpy.random.default_rng 12345)) 19 | rng 20 | (tell rng random -> ~py) 21 | (tell rng beta 1 ~py 1 ~py -> ~py) 22 | (numpy.random.beta 1 1) 23 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-numpy.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi pyffi/numpy pyffi/numpy-core 3 | ;; "../python-c-api.rkt" 4 | ;; "python-initialization.rkt" 5 | ;; "python-environment.rkt" 6 | ;; "python-evaluation.rkt" 7 | ;; "python-types.rkt" 8 | ;; ;"python-constants.rkt" 9 | ;; "python-builtins.rkt" 10 | ;; "python-operators.rkt" 11 | ;; "python-slice.rkt" 12 | racket/format 13 | racket/match) 14 | (require (for-syntax racket/base syntax/parse)) 15 | 16 | ;;; 17 | ;;; Start Python and import "numpy" 18 | ;;; 19 | 20 | (initialize) ; handles `main` and `builtins` 21 | (initialize-numpy) ; load the `numpy` module 22 | ; ; import and initialize numpy before 23 | ; ; running the delayed initializers 24 | (finish-initialization) ; run delayed setters 25 | (declare-special-prefix numpy) 26 | 27 | ;;; 28 | ;;; Tests 29 | ;;; 30 | 31 | 32 | ;;; Construction and Attributes 33 | 34 | (numpy.zeros #(2 3)) 35 | (numpy.ndarray.tolist (numpy.zeros #(2 3))) 36 | int16 37 | (numpy.ndarray.tolist (numpy.array '((1 2 3) (4 5 6)) #:dtype int16)) 38 | (numpy.ndarray.tolist (numpy.array '((0 2 3) (4 5 6)) #:dtype double)) 39 | (numpy.empty #(0 1) #:dtype double #:order "C") 40 | (define A (numpy.array '[[1 2 3 4] 41 | [5 6 7 8]])) 42 | ; attributes 43 | (equal? (.ndim A) 2) ; A.ndim 44 | (equal? (.shape A) #(2 4)) ; A.shape 45 | (equal? (.shape A) #(2 4)) 46 | (equal? (.size A) 8) ; A.size 47 | (equal? (obj-type-name (.dtype A)) "dtype[int64]") ; A.dtype 48 | (equal? (.itemsize A) 8) ; A.itemsize 49 | (equal? (obj-type-name (.data A)) "memoryview") ; A.data 50 | 51 | 52 | (define B (numpy.array '[[1.1 2.2 3.3] 53 | [4.4 5.5 6.6]])) 54 | (equal? (.ndim B) 2) ; B.ndim 55 | (equal? (.shape B) #(2 3)) ; B.shape 56 | (equal? (.size B) 6) ; B.size 57 | (equal? (.itemsize B) 8) ; B.itemsize 58 | (equal? (obj-type-name (.dtype B)) "dtype[float64]") ; B.dtype 59 | (equal? (obj-type-name (.data B)) "memoryview") ; B.data 60 | 61 | 62 | ; The low-level constructor 63 | #;(displayln (repr (numpy.ndarray ; #:shape #(2,2) 64 | #:dtype float64 #:order #\F))) 65 | 66 | 67 | 68 | ;;; Helpers 69 | 70 | (define (array A) (numpy.array A)) 71 | (define (arange n) (numpy.arange n)) 72 | 73 | 74 | ;; Numpy scalars behave as an `ndarray` so we need to use `item` 75 | ;; to convert the value from a numpy scalar to a Python scalar. 76 | (define (.item A [type ~py]) (tell A item -> type)) 77 | (define (.reshape A shape) (tell A reshape shape ~py -> ~ndarray)) 78 | 79 | (define (from-array A) 80 | (define s (.shape A)) 81 | (match s 82 | [(vector) (A .item)] 83 | [(vector n) (pylist->list (numpy.ndarray.tolist A))] 84 | [(vector m n) (for/list ([i m]) 85 | (pylist->list (numpy.ndarray.tolist (ref A i))))] 86 | [(vector m n o) (for/list ([i m]) 87 | (define Ai (ref A i)) 88 | (for/list ([j n]) 89 | (pylist->list (numpy.ndarray.tolist (ref Ai j)))))] 90 | [_ 91 | (displayln s) 92 | (error 'from-array "todo")])) 93 | 94 | ;;; Methods 95 | 96 | 97 | (procedure-arity PyObject_CallMethodObjArgs) 98 | 99 | 'METHODS 100 | 'all 101 | ((numpy.all (array '[[1 2 3 4] [5 6 7 8]])) .item) 102 | (equal? ((numpy.all (array '[[1 2 3 4] [5 6 7 8]])) .item) #t) 103 | (equal? ((numpy.all (array '[[1 2 3 4] [0 6 7 8]])) .item) #f) 104 | (equal? ((numpy.all (array '[[#t #f] [#t #t]])) .item) #f) 105 | (equal? (from-array (numpy.all (array '[[#t #f] [#t #t]]) #:axis 0)) '(#t #f)) 106 | (equal? (from-array (numpy.all (array '[-1 4 5]))) #t) 107 | ; todo [crash] (numpy.all (numpy.array '[[#t #t] [#f #t]] #:where (array '[[#t] [#f]]))) 108 | 'any 109 | (equal? ((numpy.any (array '[[1 2 3 4] [0 6 7 8]])) .item) #t) 110 | (equal? ((numpy.any (array '[[#t #f] [#t #t]])) .item) #t) 111 | (equal? (from-array (numpy.any (array '[[#t #f] [#f #f]]) #:axis 0)) '(#t #f)) 112 | (equal? (from-array (numpy.any (array '[-1 0 5]))) #t) 113 | ; todo [crash] (numpy.all (numpy.array '[[#t #t] [#f #t]] #:where (array '[[#t] [#f]]))) 114 | 'arange 115 | (equal? (from-array ((arange 6) .reshape #(2 3))) '((0 1 2) (3 4 5))) 116 | 'argmax 117 | (let ([A (add ((arange 6) .reshape #(2 3)) 10)]) 118 | (and (equal? (from-array A) '((10 11 12) (13 14 15))) 119 | (equal? (from-array (numpy.argmax A #:axis 0)) '(1 1 1)) 120 | (equal? (from-array (numpy.argmax A #:axis 1)) '(2 2)))) 121 | 'argmin 122 | (let ([A (add ((arange 6) .reshape #(2 3)) 10)]) 123 | (and (equal? (from-array A) '((10 11 12) (13 14 15))) 124 | (equal? (numpy.argmin A) 0) 125 | (equal? (from-array (numpy.argmin A #:axis 0)) '(0 0 0)) 126 | (equal? (from-array (numpy.argmin A #:axis 1)) '(0 0)))) 127 | 'argpartition 128 | (let ([A (array '[3 4 2 1])]) 129 | (and (equal? (from-array (getitem A (numpy.argpartition A 3))) '(2 1 3 4)) 130 | (equal? (from-array (getitem A (numpy.argpartition A #(1 3)))) '(1 2 3 4)))) 131 | 'argsort 132 | (equal? (from-array (numpy.argsort (array '[3 1 2]))) '(1 2 0)) 133 | 'astype 134 | (equal? (from-array (numpy.ndarray.astype (array '[1. 2. 3.]) int32)) '(1 2 3)) 135 | 'byteswap 136 | (equal? (from-array (numpy.array '[1 256 8755] #:dtype int16)) '[1 256 8755]) 137 | (let ([A (numpy.array '[1 256 8755] #:dtype int16)]) 138 | (equal? (from-array (numpy.ndarray.byteswap A #:inplace #t)) '(256 1 13090))) 139 | 'choose 140 | (let ([choices '[[ 0 1 2 3] [10 11 12 13] [20 21 22 23] [30 31 32 33]]]) 141 | (equal? (from-array (numpy.choose '[2 3 1 0] choices)) '(20 31 12 3))) 142 | 'clip 143 | (equal? (from-array (numpy.clip (arange 10) 1 8)) '(1 1 2 3 4 5 6 7 8 8)) 144 | 'compress 145 | (equal? (from-array (numpy.compress '[0 1] (array '[[1 2] [3 4] [5 6]]) #:axis 0)) '((3 4))) 146 | '----- 147 | 'diag 148 | (equal? (from-array (numpy.diag (array '[[1 2] [3 4] [5 6]]))) '(1 4)) 149 | (equal? (from-array (numpy.diag ((arange 9) .reshape #(3 3)))) '(0 4 8)) 150 | (equal? (from-array (numpy.diag ((arange 9) .reshape #(3 3)) #:k 1)) '(1 5)) 151 | (equal? (from-array (numpy.diag ((arange 9) .reshape #(3 3)) #:k -1)) '(3 7)) 152 | (equal? (from-array (numpy.diag '[1 2])) '[[1 0] [0 2]]) 153 | 'full 154 | (equal? (from-array (numpy.full #(2 3) 4)) '((4 4 4) (4 4 4))) 155 | (equal? (from-array (numpy.full #(2 3) 4.)) '((4. 4. 4.) (4. 4. 4.))) 156 | (equal? (from-array (numpy.full #(2 3) +nan.0)) '((+nan.0 +nan.0 +nan.0) (+nan.0 +nan.0 +nan.0))) 157 | 'interp 158 | (let ([xp (array '[1 2 3])] [fp (array '[3 2 0])]) 159 | (equal? (from-array (numpy.interp 2.5 xp fp)) 1.0)) 160 | 'isnan 161 | (equal? (from-array (numpy.isnan +nan.0)) #t) 162 | (equal? (from-array (numpy.isnan 42)) #f) 163 | 164 | 165 | 166 | '----FFT---- 167 | #;'fft.fft 168 | #;(equal? (from-array (numpy.fft.fft (arange 10))) 169 | '(45.0+0.0i -5.0+15.3884177i -5.0+6.8819096i 170 | -5.0+3.63271264i -5.0+1.62459848i -5.0-1.33226763e-15i 171 | -5.0-1.62459848i -5.0-3.63271264i -5.0-6.8819096i 172 | -5.0-15.3884177i)) 173 | 174 | 175 | '----RANDOM---- 176 | (numpy.random.seed 42) 177 | (let ([A (numpy.random.choice (array '[#f #t]) #:size 10)]) 178 | ; count falst to true transitions: 179 | (numpy.count_nonzero (lt (ref A (slice 1 #f)) (ref A (slice #f -1))))) 180 | 181 | (let ([prices (numpy.full 100 +nan.0)]) 182 | (:= prices [(array '[0 25 60 -1])] (array '[80. 30. 75. 50.])) 183 | (define x (arange (len prices))) 184 | (define is-valid (invert (numpy.isnan prices))) 185 | ; prices = np.interp(x=x, xp=x[is_valid], fp=prices[is_valid]) 186 | (set! prices (numpy.interp x (getitem x is-valid) (getitem prices is-valid))) 187 | (set! prices (add prices (mul (numpy.random.randn (len prices)) 2.))) 188 | (from-array prices)) 189 | 190 | 191 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-pyautogui.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi) 3 | 4 | ;; Setup Python 5 | (initialize) 6 | (finish-initialization) 7 | 8 | 9 | ;; Import `pyautogui` 10 | (import pyautogui) 11 | 12 | ;; Use it 13 | (pyautogui.position) 14 | (pyautogui.write "echo \"Hello World\" \n") 15 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-python-c-api.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require pyffi/python-c-api 4 | pyffi/python-initialization 5 | pyffi/python-environment 6 | pyffi/python-evaluation 7 | pyffi/python-types) 8 | 9 | (Py_Initialize) 10 | 11 | ;;; Informative functions: 12 | (map displayln 13 | (list (list 'is-initialized? (Py_IsInitialized)) 14 | (list 'version (Py_GetVersion)) 15 | (list 'platform (Py_GetPlatform)) 16 | (list 'compiler (Py_GetCompiler)) 17 | (list 'build-info (Py_GetBuildInfo)) 18 | (list 'copyright (Py_GetCopyright)))) 19 | 20 | 21 | ;;; 22 | ;;; Integer Objects 23 | ;;; 24 | 25 | ; These all return 256 (or 256.0). 26 | (PyLong_AsLong (PyLong_FromLong 256)) 27 | (PyLong_AsLong (PyLong_FromLongLong 256)) 28 | (PyLong_AsLong (PyLong_FromUnsignedLongLong 256)) 29 | (PyLong_AsLong (PyLong_FromDouble 256.0)) 30 | (PyLong_AsLong (PyLong_FromString "256" #f 10)) 31 | (PyLong_AsLongLong (PyLong_FromLong 256)) 32 | (PyLong_AsUnsignedLong (PyLong_FromLong 256)) 33 | (PyLong_AsUnsignedLongLong (PyLong_FromLong 256)) 34 | (PyLong_AsDouble (PyLong_FromLong 256)) 35 | (PyLong_AsLong (PyLong_FromVoidPtr (PyLong_AsVoidPtr 36 | (PyLong_FromLong 256)))) 37 | 38 | ;;; 39 | ;;; List Objects 40 | ;;; 41 | 42 | ; (PyList_New 5) 43 | 44 | ; These all evaluate to #t 45 | (equal? (PyList_Size (PyList_New 5)) 46 | 5) 47 | (equal? (let () 48 | (define l (PyList_New 5)) 49 | (PyList_SetItem l 0 (PyLong_FromLong 256)) 50 | (PyLong_AsLong (PyList_GetItem l 0))) 51 | 256) 52 | (equal? (let () 53 | (define l (PyList_New 5)) 54 | (PyList_Insert l 0 (PyLong_FromLong 256)) 55 | (PyList_Insert l 0 (PyLong_FromLong 255)) 56 | (PyList_Insert l 0 (PyLong_FromLong 254)) 57 | (list (PyLong_AsLong (PyList_GetItem l 0)) 58 | (PyLong_AsLong (PyList_GetItem l 1)) 59 | (PyLong_AsLong (PyList_GetItem l 2)))) 60 | '(254 255 256)) 61 | (equal? (let () 62 | (define l (PyList_New 0)) 63 | (PyList_Append l (PyLong_FromLong 254)) 64 | (PyList_Append l (PyLong_FromLong 255)) 65 | (PyList_Append l (PyLong_FromLong 256)) 66 | (list (PyLong_AsLong (PyList_GetItem l 0)) 67 | (PyLong_AsLong (PyList_GetItem l 1)) 68 | (PyLong_AsLong (PyList_GetItem l 2)))) 69 | '(254 255 256)) 70 | (equal? (let () 71 | (define l (PyList_New 0)) 72 | (PyList_Append l (PyLong_FromLong 254)) 73 | (PyList_Append l (PyLong_FromLong 255)) 74 | (PyList_Append l (PyLong_FromLong 256)) 75 | (PyList_Append l (PyLong_FromLong 257)) 76 | (PyList_Append l (PyLong_FromLong 258)) 77 | (PyList_Append l (PyLong_FromLong 259)) 78 | (set! l (PyList_GetSlice l 1 3)) 79 | (list (PyLong_AsLong (PyList_GetItem l 0)) 80 | (PyLong_AsLong (PyList_GetItem l 1)))) 81 | '(255 256)) 82 | (equal? (let () 83 | (define l (PyList_New 0)) 84 | (PyList_Append l (PyLong_FromLong 100)) 85 | (PyList_Append l (PyLong_FromLong 101)) 86 | (PyList_Append l (PyLong_FromLong 102)) 87 | (PyList_Append l (PyLong_FromLong 103)) 88 | (PyList_Append l (PyLong_FromLong 104)) 89 | (PyList_Append l (PyLong_FromLong 105)) 90 | (define m (PyList_New 0)) 91 | (PyList_Append m (PyLong_FromLong 200)) 92 | (PyList_Append m (PyLong_FromLong 201)) 93 | (PyList_Append m (PyLong_FromLong 202)) 94 | (PyList_SetSlice l 2 3 m) 95 | (for/list ([i 6]) 96 | (PyLong_AsLong (PyList_GetItem l i)))) 97 | '(100 101 200 201 202 103)) 98 | (equal? (let () 99 | (define l (PyList_New 0)) 100 | (PyList_Append l (PyLong_FromLong 190)) 101 | (PyList_Append l (PyLong_FromLong 181)) 102 | (PyList_Append l (PyLong_FromLong 172)) 103 | (PyList_Append l (PyLong_FromLong 163)) 104 | (PyList_Append l (PyLong_FromLong 154)) 105 | (PyList_Append l (PyLong_FromLong 145)) 106 | (PyList_Sort l) 107 | (for/list ([i 6]) 108 | (PyLong_AsLong (PyList_GetItem l i)))) 109 | '(145 154 163 172 181 190)) 110 | 111 | (initialize) 112 | (finish-initialization) 113 | 114 | (void (run* "import builtins")) 115 | (displayln "b") (newline) 116 | (define b (PyDict_GetItemString globals "builtins")) 117 | (displayln (python->racket (PyObject_Repr b))) 118 | 119 | (displayln "d") (newline) 120 | (define d (PyModule_GetDict b)) 121 | (void (python->racket (PyObject_Repr d))) 122 | (python->racket (PyDict_GetItemString d "repr")) 123 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-reference-counting.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require pyffi) 4 | 5 | (initialize) 6 | (post-initialize) 7 | 8 | (define x (PyBool_FromLong 0)) 9 | (collect-garbage) 10 | (set! x #f) 11 | (collect-garbage) 12 | 13 | 'foo 14 | (define xs (pylist)) 15 | (collect-garbage) 16 | (set! xs #f) 17 | (collect-garbage) 18 | (sleep 10) 19 | 20 | 21 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-signature.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require (for-syntax racket/base syntax/parse racket/require-transform)) 3 | 4 | (require 5 | pyffi/structs 6 | pyffi/python-c-api 7 | pyffi/python-initialization 8 | pyffi/python-environment 9 | pyffi/python-evaluation 10 | pyffi/python-types 11 | pyffi/python-builtins 12 | pyffi/python-operators 13 | pyffi/python-attributes 14 | pyffi/python-functions 15 | racket/format 16 | racket/list 17 | racket/match) 18 | 19 | 20 | 21 | ;;; 22 | ;;; Start Python and import "numpy" 23 | ;;; 24 | 25 | (initialize) ; handles `main` and `builtins` 26 | ;(import-inspect) 27 | ;(require "numpy.rkt") 28 | ;(initialize-numpy) ; load the `numpy` module 29 | ; ; import and initialize numpy before 30 | ; ; running the delayed initializers 31 | (finish-initialization) ; run delayed setters 32 | 33 | 34 | (define-syntax (define-predicates stx) 35 | (syntax-case stx () 36 | [(_ id ...) 37 | (syntax/loc stx 38 | (begin 39 | (define-py id (~fun ~py -> ~py)) 40 | ...))])) 41 | 42 | (define-predicates 43 | ; inspect.ismodule 44 | inspect.isclass 45 | inspect.ismethod 46 | inspect.isfunction 47 | inspect.isgeneratorfunction 48 | inspect.isgenerator 49 | inspect.iscoroutinefunction 50 | inspect.iscoroutine 51 | inspect.isawaitable 52 | inspect.isasyncgenfunction 53 | inspect.isasyncgen 54 | inspect.istraceback 55 | inspect.isframe 56 | inspect.iscode 57 | inspect.isbuiltin 58 | inspect.isroutine 59 | inspect.isabstract 60 | inspect.ismethoddescriptor 61 | inspect.isgetsetdescriptor 62 | inspect.ismemberdescriptor) 63 | 64 | (define-syntax (define-getters stx) 65 | (syntax-case stx () 66 | [(_ id ...) 67 | (syntax/loc stx 68 | (begin 69 | (define-py id (~fun ~py -> ~py)) 70 | ...))])) 71 | 72 | (define-getters 73 | inspect.getdoc 74 | inspect.getcomments 75 | inspect.getfile 76 | inspect.getmodule 77 | inspect.getsourcefile 78 | inspect.getsourcelines 79 | inspect.getsource) 80 | 81 | (define-py inspect.cleandoc (~fun ~py -> ~py)) 82 | ;; (define-py inspect.signature (~fun ~py -> ~py)) 83 | ;; (finish-initialization) 84 | 85 | (finish-initialization) 86 | (void (inspect.getmembers (obj "pyfun" (get 'inspect.getmembers)))) 87 | (void (inspect.cleandoc (inspect.getdoc (obj "pyfun" (get 'inspect.getmembers))))) 88 | 89 | 90 | ;; (~pyprocedure 91 | ;; input-types ; list of pytype 92 | ;; output-type 93 | ;; keywords ; #f or list of symbols 94 | ;; keyword-types ; #f or list of ptype 95 | ;; optional-after ; if non-#f, the index of the first optional argument 96 | ;; ) 97 | 98 | 99 | 100 | ; (struct pyproc (positional-parameters positional-excess keyword-parameters keyword-excess)) 101 | ; (struct pyprocedure (input-types output-type keywords keyword-types optional-after) #:transparent) 102 | 103 | ;; (struct pyproc (positional-parameters positional-types positional-excess 104 | ;; keyword-parameters keyword-types keyword-excess 105 | ;; first-optional result-type) 106 | ;; #:transparent) 107 | 108 | 109 | 110 | 111 | ;; (define p (get-fun 'inspect.ismodule)) 112 | ;; (displayln (list 'HERE p)) 113 | ;; (define-py inspect.ismodule p) 114 | ;; (define-py builtins.repr p) 115 | ;; (finish-initialization) 116 | 117 | ;; (builtins.repr 42) 118 | ; (inspect.ismodule 42) 119 | 120 | 121 | 122 | ;; (define transpose (pyproc/keywords (get-fun-as-pyproc 'numpy.transpose))) 123 | ;; ;(define array (pyproc/keywords (get-fun-as-pyproc 'numpy.array))) 124 | 125 | ;; array 126 | ;; (array '[[1 2] [3 4]]) 127 | ;(transpose ) 128 | 129 | ;(get-fun 'inspect.signature) 130 | 131 | ; (struct pyprocedure (input-types output-type keywords keyword-types optional-after)) 132 | 133 | #;(for/list ([i (len ps)]) (getitem ps i)) 134 | #;(list (.empty s) 135 | (len ) 136 | (.return_annotation s)) 137 | 138 | ; inspect.signature(inspect.signature).parameters.values() 139 | ; inspect.signature(inspect.signature).parameters.items() 140 | 141 | 142 | 143 | 144 | ; >>> import inspect 145 | ; >>> inspect.signature(inspect.signature) 146 | 147 | ; 148 | 149 | ;; POSITIONAL_ONLY 150 | ;; Value must be supplied as a positional argument. Positional only 151 | ;; parameters are those which appear before a / entry (if present) in a 152 | ;; Python function definition. 153 | 154 | ;; POSITIONAL_OR_KEYWORD 155 | ;; Value may be supplied as either a keyword or positional argument (this 156 | ;; is the standard binding behaviour for functions implemented in 157 | ;; Python.) 158 | 159 | ;; VAR_POSITIONAL 160 | ;; A tuple of positional arguments that aren’t bound to any other 161 | ;; parameter. This corresponds to a *args parameter in a Python function 162 | ;; definition. 163 | 164 | ;; KEYWORD_ONLY 165 | ;; Value must be supplied as a keyword argument. Keyword only parameters 166 | ;; are those which appear after a * or *args entry in a Python function 167 | ;; definition. 168 | 169 | ;; VAR_KEYWORD 170 | ;; A dict of keyword arguments that aren’t bound to any other 171 | ;; parameter. This corresponds to a **kwargs parameter in a Python 172 | ;; function definition. 173 | 174 | 175 | ;; (empty #(struct:obj "type" #) 176 | ;; name "obj" 177 | ;; default Empty 178 | ;; annotation #(struct:obj "type" #) 179 | ;; kind #(struct:obj "_ParameterKind" #) 180 | ;; kind.description "positional or keyword") 181 | 182 | ;; (empty #(struct:obj "type" #) 183 | ;; name "follow_wrapped" 184 | ;; default #t 185 | ;; annotation #(struct:obj "type" #) 186 | ;; kind #(struct:obj "_ParameterKind" #) 187 | ;; kind.description "keyword-only") 188 | 189 | ;; (empty #(struct:obj "type" #) 190 | ;; name "globals" 191 | ;; default None 192 | ;; annotation #(struct:obj "type" #) 193 | ;; kind #(struct:obj "_ParameterKind" #) 194 | ;; kind.description "keyword-only") 195 | 196 | ;; (empty #(struct:obj "type" #) 197 | ;; name "locals" 198 | ;; default None 199 | ;; annotation #(struct:obj "type" #) 200 | ;; kind #(struct:obj "_ParameterKind" #) 201 | ;; kind.description "keyword-only") 202 | 203 | ;; (empty #(struct:obj "type" #) 204 | ;; name "eval_str" 205 | ;; default #f 206 | ;; annotation #(struct:obj "type" #) 207 | ;; kind #(struct:obj "_ParameterKind" #) 208 | ;; kind.description "keyword-only") 209 | 210 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-slice.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | 4 | (initialize) 5 | (import-numpy) 6 | (finish-initialization) 7 | 8 | 9 | (slice 1) 10 | (displayln (slice 1)) 11 | (write (slice 1)) (newline) 12 | 13 | (slice-indices (slice 1 2 3) 10) 14 | 15 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-special.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi/python-attributes) 3 | (provide (all-from-out pyffi/python-attributes)) 4 | 5 | (test-special foo) 6 | (test-special bar) 7 | 8 | (declare-special-prefix foo) 9 | 10 | (test-special foo) 11 | (test-special bar) 12 | 13 | 'here 14 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-special2.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require "test-special.rkt") 3 | 4 | (test-special foo) 5 | (test-special bar) 6 | 7 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-string.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | 4 | (initialize) 5 | (import-numpy) 6 | (finish-initialization) 7 | 8 | (string->pystring "foo") 9 | (pystring->string (string->pystring "foo")) 10 | 11 | (pystring-length (string->pystring "foo")) 12 | 13 | (pystring-ref (string->pystring "foo") 0) 14 | (pystring-ref (string->pystring "foo") 1) 15 | (pystring-ref (string->pystring "foo") 2) 16 | 17 | (subpystring (string->pystring "foobarbaz") 3 6) 18 | 19 | (pystring-slice (string->pystring "0123456789abcedf") 2 10 2) 20 | 21 | (for/list ([x (in-pystring (string->pystring "foo"))]) x) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /pyffi-tests/tests/test-write-and-display.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi) 3 | 4 | (define hw (string->pystring "Hello World")) 5 | 6 | (write hw) (newline) 7 | (display hw) (newline) 8 | -------------------------------------------------------------------------------- /pyffi-tutorials/example-pygame-bouncing-ball.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi) 3 | 4 | (initialize) 5 | (post-initialize) 6 | 7 | (import pygame) 8 | ;(import pygame.display as pd) 9 | ;(import pygame.image as pi) 10 | 11 | (pygame.init) 12 | 13 | (define width 320) 14 | (define height 240) 15 | (define size (pytuple width height)) 16 | 17 | (define speed (pylist 2 2)) 18 | (define black (pytuple 0 0 0)) 19 | 20 | ;(define screen (pygame.display.set_mode size)) ; todo 21 | (define screen (pygame.display .set_mode size)) 22 | (define ball (pygame.image .load "pygame-intro-ball.gif")) 23 | (define ballrect (ball .get_rect)) 24 | 25 | (define (flip-sign i) 26 | (pylist-set! speed i (- (pylist-ref speed i)))) 27 | 28 | (let loop () 29 | (for ([event (in-pylist (pygame.event .get))]) 30 | (display "." (current-error-port)) 31 | (when (= event.type pygame.QUIT) 32 | (exit)) 33 | 34 | (set! ballrect (ballrect .move speed)) 35 | (when (or (< ballrect.left 0) 36 | (> ballrect.right width)) 37 | (flip-sign 0)) 38 | (when (or (< ballrect.top 0) 39 | (> ballrect.bottom height)) 40 | (flip-sign 1)) 41 | 42 | (screen .fill black) 43 | (screen .blit ball ballrect) 44 | (pygame.display .flip)) 45 | (loop)) 46 | 47 | 48 | ;; import sys, pygame 49 | ;; pygame.init() 50 | 51 | ;; size = width, height = 320, 240 52 | ;; speed = [2, 2] 53 | ;; black = 0, 0, 0 54 | 55 | ;; screen = pygame.display.set_mode(size) 56 | 57 | ;; ball = pygame.image.load("intro_ball.gif") 58 | ;; ballrect = ball.get_rect() 59 | 60 | ;; while 1: 61 | ;; for event in pygame.event.get(): 62 | ;; if event.type == pygame.QUIT: sys.exit() 63 | 64 | ;; ballrect = ballrect.move(speed) 65 | ;; if ballrect.left < 0 or ballrect.right > width: 66 | ;; speed[0] = -speed[0] 67 | ;; if ballrect.top < 0 or ballrect.bottom > height: 68 | ;; speed[1] = -speed[1] 69 | 70 | ;; screen.fill(black) 71 | ;; screen.blit(ball, ballrect) 72 | ;; pygame.display.flip() 73 | -------------------------------------------------------------------------------- /pyffi-tutorials/fish.csv: -------------------------------------------------------------------------------- 1 | Species,Weight,Length1,Length2,Length3,Height,Width 2 | Bream,242,23.2,25.4,30,11.52,4.02 3 | Bream,290,24,26.3,31.2,12.48,4.3056 4 | Bream,340,23.9,26.5,31.1,12.3778,4.6961 5 | Bream,363,26.3,29,33.5,12.73,4.4555 6 | Bream,430,26.5,29,34,12.444,5.134 7 | Bream,450,26.8,29.7,34.7,13.6024,4.9274 8 | Bream,500,26.8,29.7,34.5,14.1795,5.2785 9 | Bream,390,27.6,30,35,12.67,4.69 10 | Bream,450,27.6,30,35.1,14.0049,4.8438 11 | Bream,500,28.5,30.7,36.2,14.2266,4.9594 12 | Bream,475,28.4,31,36.2,14.2628,5.1042 13 | Bream,500,28.7,31,36.2,14.3714,4.8146 14 | Bream,500,29.1,31.5,36.4,13.7592,4.368 15 | Bream,340,29.5,32,37.3,13.9129,5.0728 16 | Bream,600,29.4,32,37.2,14.9544,5.1708 17 | Bream,600,29.4,32,37.2,15.438,5.58 18 | Bream,700,30.4,33,38.3,14.8604,5.2854 19 | Bream,700,30.4,33,38.5,14.938,5.1975 20 | Bream,610,30.9,33.5,38.6,15.633,5.1338 21 | Bream,650,31,33.5,38.7,14.4738,5.7276 22 | Bream,575,31.3,34,39.5,15.1285,5.5695 23 | Bream,685,31.4,34,39.2,15.9936,5.3704 24 | Bream,620,31.5,34.5,39.7,15.5227,5.2801 25 | Bream,680,31.8,35,40.6,15.4686,6.1306 26 | Bream,700,31.9,35,40.5,16.2405,5.589 27 | Bream,725,31.8,35,40.9,16.36,6.0532 28 | Bream,720,32,35,40.6,16.3618,6.09 29 | Bream,714,32.7,36,41.5,16.517,5.8515 30 | Bream,850,32.8,36,41.6,16.8896,6.1984 31 | Bream,1000,33.5,37,42.6,18.957,6.603 32 | Bream,920,35,38.5,44.1,18.0369,6.3063 33 | Bream,955,35,38.5,44,18.084,6.292 34 | Bream,925,36.2,39.5,45.3,18.7542,6.7497 35 | Bream,975,37.4,41,45.9,18.6354,6.7473 36 | Bream,950,38,41,46.5,17.6235,6.3705 37 | Roach,40,12.9,14.1,16.2,4.1472,2.268 38 | Roach,69,16.5,18.2,20.3,5.2983,2.8217 39 | Roach,78,17.5,18.8,21.2,5.5756,2.9044 40 | Roach,87,18.2,19.8,22.2,5.6166,3.1746 41 | Roach,120,18.6,20,22.2,6.216,3.5742 42 | Roach,0,19,20.5,22.8,6.4752,3.3516 43 | Roach,110,19.1,20.8,23.1,6.1677,3.3957 44 | Roach,120,19.4,21,23.7,6.1146,3.2943 45 | Roach,150,20.4,22,24.7,5.8045,3.7544 46 | Roach,145,20.5,22,24.3,6.6339,3.5478 47 | Roach,160,20.5,22.5,25.3,7.0334,3.8203 48 | Roach,140,21,22.5,25,6.55,3.325 49 | Roach,160,21.1,22.5,25,6.4,3.8 50 | Roach,169,22,24,27.2,7.5344,3.8352 51 | Roach,161,22,23.4,26.7,6.9153,3.6312 52 | Roach,200,22.1,23.5,26.8,7.3968,4.1272 53 | Roach,180,23.6,25.2,27.9,7.0866,3.906 54 | Roach,290,24,26,29.2,8.8768,4.4968 55 | Roach,272,25,27,30.6,8.568,4.7736 56 | Roach,390,29.5,31.7,35,9.485,5.355 57 | Whitefish,270,23.6,26,28.7,8.3804,4.2476 58 | Whitefish,270,24.1,26.5,29.3,8.1454,4.2485 59 | Whitefish,306,25.6,28,30.8,8.778,4.6816 60 | Whitefish,540,28.5,31,34,10.744,6.562 61 | Whitefish,800,33.7,36.4,39.6,11.7612,6.5736 62 | Whitefish,1000,37.3,40,43.5,12.354,6.525 63 | Parkki,55,13.5,14.7,16.5,6.8475,2.3265 64 | Parkki,60,14.3,15.5,17.4,6.5772,2.3142 65 | Parkki,90,16.3,17.7,19.8,7.4052,2.673 66 | Parkki,120,17.5,19,21.3,8.3922,2.9181 67 | Parkki,150,18.4,20,22.4,8.8928,3.2928 68 | Parkki,140,19,20.7,23.2,8.5376,3.2944 69 | Parkki,170,19,20.7,23.2,9.396,3.4104 70 | Parkki,145,19.8,21.5,24.1,9.7364,3.1571 71 | Parkki,200,21.2,23,25.8,10.3458,3.6636 72 | Parkki,273,23,25,28,11.088,4.144 73 | Parkki,300,24,26,29,11.368,4.234 74 | Perch,5.9,7.5,8.4,8.8,2.112,1.408 75 | Perch,32,12.5,13.7,14.7,3.528,1.9992 76 | Perch,40,13.8,15,16,3.824,2.432 77 | Perch,51.5,15,16.2,17.2,4.5924,2.6316 78 | Perch,70,15.7,17.4,18.5,4.588,2.9415 79 | Perch,100,16.2,18,19.2,5.2224,3.3216 80 | Perch,78,16.8,18.7,19.4,5.1992,3.1234 81 | Perch,80,17.2,19,20.2,5.6358,3.0502 82 | Perch,85,17.8,19.6,20.8,5.1376,3.0368 83 | Perch,85,18.2,20,21,5.082,2.772 84 | Perch,110,19,21,22.5,5.6925,3.555 85 | Perch,115,19,21,22.5,5.9175,3.3075 86 | Perch,125,19,21,22.5,5.6925,3.6675 87 | Perch,130,19.3,21.3,22.8,6.384,3.534 88 | Perch,120,20,22,23.5,6.11,3.4075 89 | Perch,120,20,22,23.5,5.64,3.525 90 | Perch,130,20,22,23.5,6.11,3.525 91 | Perch,135,20,22,23.5,5.875,3.525 92 | Perch,110,20,22,23.5,5.5225,3.995 93 | Perch,130,20.5,22.5,24,5.856,3.624 94 | Perch,150,20.5,22.5,24,6.792,3.624 95 | Perch,145,20.7,22.7,24.2,5.9532,3.63 96 | Perch,150,21,23,24.5,5.2185,3.626 97 | Perch,170,21.5,23.5,25,6.275,3.725 98 | Perch,225,22,24,25.5,7.293,3.723 99 | Perch,145,22,24,25.5,6.375,3.825 100 | Perch,188,22.6,24.6,26.2,6.7334,4.1658 101 | Perch,180,23,25,26.5,6.4395,3.6835 102 | Perch,197,23.5,25.6,27,6.561,4.239 103 | Perch,218,25,26.5,28,7.168,4.144 104 | Perch,300,25.2,27.3,28.7,8.323,5.1373 105 | Perch,260,25.4,27.5,28.9,7.1672,4.335 106 | Perch,265,25.4,27.5,28.9,7.0516,4.335 107 | Perch,250,25.4,27.5,28.9,7.2828,4.5662 108 | Perch,250,25.9,28,29.4,7.8204,4.2042 109 | Perch,300,26.9,28.7,30.1,7.5852,4.6354 110 | Perch,320,27.8,30,31.6,7.6156,4.7716 111 | Perch,514,30.5,32.8,34,10.03,6.018 112 | Perch,556,32,34.5,36.5,10.2565,6.3875 113 | Perch,840,32.5,35,37.3,11.4884,7.7957 114 | Perch,685,34,36.5,39,10.881,6.864 115 | Perch,700,34,36,38.3,10.6091,6.7408 116 | Perch,700,34.5,37,39.4,10.835,6.2646 117 | Perch,690,34.6,37,39.3,10.5717,6.3666 118 | Perch,900,36.5,39,41.4,11.1366,7.4934 119 | Perch,650,36.5,39,41.4,11.1366,6.003 120 | Perch,820,36.6,39,41.3,12.4313,7.3514 121 | Perch,850,36.9,40,42.3,11.9286,7.1064 122 | Perch,900,37,40,42.5,11.73,7.225 123 | Perch,1015,37,40,42.4,12.3808,7.4624 124 | Perch,820,37.1,40,42.5,11.135,6.63 125 | Perch,1100,39,42,44.6,12.8002,6.8684 126 | Perch,1000,39.8,43,45.2,11.9328,7.2772 127 | Perch,1100,40.1,43,45.5,12.5125,7.4165 128 | Perch,1000,40.2,43.5,46,12.604,8.142 129 | Perch,1000,41.1,44,46.6,12.4888,7.5958 130 | Pike,200,30,32.3,34.8,5.568,3.3756 131 | Pike,300,31.7,34,37.8,5.7078,4.158 132 | Pike,300,32.7,35,38.8,5.9364,4.3844 133 | Pike,300,34.8,37.3,39.8,6.2884,4.0198 134 | Pike,430,35.5,38,40.5,7.29,4.5765 135 | Pike,345,36,38.5,41,6.396,3.977 136 | Pike,456,40,42.5,45.5,7.28,4.3225 137 | Pike,510,40,42.5,45.5,6.825,4.459 138 | Pike,540,40.1,43,45.8,7.786,5.1296 139 | Pike,500,42,45,48,6.96,4.896 140 | Pike,567,43.2,46,48.7,7.792,4.87 141 | Pike,770,44.8,48,51.2,7.68,5.376 142 | Pike,950,48.3,51.7,55.1,8.9262,6.1712 143 | Pike,1250,52,56,59.7,10.6863,6.9849 144 | Pike,1600,56,60,64,9.6,6.144 145 | Pike,1550,56,60,64,9.6,6.144 146 | Pike,1650,59,63.4,68,10.812,7.48 147 | Smelt,6.7,9.3,9.8,10.8,1.7388,1.0476 148 | Smelt,7.5,10,10.5,11.6,1.972,1.16 149 | Smelt,7,10.1,10.6,11.6,1.7284,1.1484 150 | Smelt,9.7,10.4,11,12,2.196,1.38 151 | Smelt,9.8,10.7,11.2,12.4,2.0832,1.2772 152 | Smelt,8.7,10.8,11.3,12.6,1.9782,1.2852 153 | Smelt,10,11.3,11.8,13.1,2.2139,1.2838 154 | Smelt,9.9,11.3,11.8,13.1,2.2139,1.1659 155 | Smelt,9.8,11.4,12,13.2,2.2044,1.1484 156 | Smelt,12.2,11.5,12.2,13.4,2.0904,1.3936 157 | Smelt,13.4,11.7,12.4,13.5,2.43,1.269 158 | Smelt,12.2,12.1,13,13.8,2.277,1.2558 159 | Smelt,19.7,13.2,14.3,15.2,2.8728,2.0672 160 | Smelt,19.9,13.8,15,16.2,2.9322,1.8792 161 | -------------------------------------------------------------------------------- /pyffi-tutorials/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define compile-omit-paths 'all) 4 | 5 | (define deps '("base")) 6 | (define build-deps '()) 7 | 8 | (define pkg-desc "Use Python from Racket - Tutorials") 9 | 10 | (define pkg-authors '(soegaard)) 11 | 12 | (define version "1.0") 13 | 14 | (define test-responsibles '((all jensaxel@soegaard.net))) 15 | -------------------------------------------------------------------------------- /pyffi-tutorials/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soegaard/pyffi/099e45cea5739a4eae2ce2bcbb35a17de347083a/pyffi-tutorials/logo.png -------------------------------------------------------------------------------- /pyffi-tutorials/pygame-intro-ball.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soegaard/pyffi/099e45cea5739a4eae2ce2bcbb35a17de347083a/pyffi-tutorials/pygame-intro-ball.gif -------------------------------------------------------------------------------- /pyffi-tutorials/pygments/sxml-renderer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | ;; Original by Danny Yoo 3 | ;; https://github.com/shriram/scribble-embedding/blob/master/sxml-render.rkt 4 | 5 | ;; Added html->element. 6 | 7 | (require racket/match 8 | scribble/html-properties 9 | scribble/core 10 | html-parsing) 11 | 12 | 13 | (provide html->element sxml->element) 14 | 15 | (define (html->element x) 16 | (define top (cdr (html->xexp (open-input-string x)))) 17 | (map sxml->element top)) 18 | 19 | 20 | ;; sxml->element: sxml -> element 21 | ;; Embeds HTML content into a Scribble document. 22 | (define (sxml->element an-sxml) 23 | (match an-sxml 24 | [(list '& 'nbsp) 25 | 'nbsp] 26 | [(list '& sym) 27 | sym] 28 | 29 | [(list tag-name (list '@ (list attr-name attr-value) ...) children ...) 30 | (tagged->element tag-name attr-name attr-value children)] 31 | 32 | [(list tag-name children ...) 33 | (tagged->element tag-name '() '() children)] 34 | 35 | [(? symbol?) 36 | an-sxml] 37 | 38 | [(? string?) 39 | an-sxml] 40 | 41 | [(? char?) 42 | (string an-sxml)])) 43 | 44 | 45 | (define (tagged->element tag-name attr-names attr-values children) 46 | (define tag-attr (alt-tag (symbol->string tag-name))) 47 | (define attrs-attr (attributes (map cons attr-names attr-values))) 48 | (define content (map sxml->element children)) 49 | (make-element (make-style #f (list tag-attr attrs-attr)) 50 | content)) 51 | -------------------------------------------------------------------------------- /pyffi-tutorials/pygments/test-scribble-with-pygments.rkt: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | @(require scriblib/render-cond scribble/core 3 | pyffi 4 | racket/format racket/string 5 | "sxml-renderer.rkt") 6 | 7 | @(define hi #f) 8 | @(define python #f) 9 | @(define html #f) 10 | @(define swift #f) 11 | @(define js #f) 12 | @(let () 13 | ; Note: Using (let () ...) is important here. 14 | ; If (begin ...) is used, then `scribble` moves definitions 15 | ; from import-from above the initialization - which causes a crash. 16 | (initialize) 17 | (post-initialize) 18 | (import-from pygments highlight) 19 | (import-from pygments.lexers PythonLexer) 20 | (import-from pygments.lexers HtmlLexer) 21 | (import-from pygments.lexers SwiftLexer) 22 | (import-from pygments.lexers JavascriptLexer) 23 | (import-from pygments.formatters HtmlFormatter) 24 | (set! hi (λ (lexer . code) 25 | (set! code (string-append* code)) 26 | ; 27 | (cond-element 28 | [html (define h (pystring->string 29 | (highlight code lexer 30 | (HtmlFormatter #:noclasses #t)))) 31 | (html->element h)] 32 | [else ""]))) 33 | (set! python (λ code (apply hi (PythonLexer) code))) 34 | (set! html (λ code (apply hi (HtmlLexer) code))) 35 | (set! swift (λ code (apply hi (SwiftLexer) code))) 36 | (set! js (λ code (apply hi (JavascriptLexer) code)))) 37 | 38 | 39 | @bold{Python} 40 | @python{def partition(l, r, nums): 41 | # Last element will be the pivot and the first element the pointer 42 | pivot, ptr = nums[r], l 43 | for i in range(l, r): 44 | if nums[i] <= pivot: 45 | # Swapping values smaller than the pivot to the front 46 | nums[i], nums[ptr] = nums[ptr], nums[i] 47 | ptr += 1 48 | # Finally swapping the last element with the pointer indexed number 49 | nums[ptr], nums[r] = nums[r], nums[ptr] 50 | return ptr} 51 | 52 | @bold{HTML} 53 | @html{ 54 | 55 | 56 |

A Header

57 |

A paragraph.

58 | 59 | } 60 | 61 | @bold{Swift} 62 | @swift{ 63 | private func partition(_ array: inout ArraySlice) 64 | -> ArraySlice.Index { 65 | 66 | let midPoint = (array.startIndex + array.endIndex) / 2 67 | array.swapAt(midPoint, array.startIndex) 68 | let pivot = array[array.startIndex] 69 | var lower = array.startIndex 70 | var upper = array.endIndex - 1 71 | repeat { 72 | while lower < array.endIndex - 1 && array[lower] <= pivot { 73 | lower += 1 74 | } 75 | while array[upper] > pivot { 76 | upper -= 1 77 | } 78 | if lower < upper { 79 | array.swapAt(lower, upper) 80 | } 81 | } while lower < upper 82 | array.swapAt(array.startIndex, upper) 83 | return upper 84 | }} 85 | 86 | @bold{JavaScript} 87 | @js{ 88 | function quickSort(arr, left = 0, right = arr.length - 1) { 89 | if (arr.length < 2) return arr; 90 | 91 | const index = partition(arr, left, right); 92 | 93 | if (left < index - 1) { 94 | quickSort(arr, left, index - 1) 95 | } 96 | if (right > index) { 97 | quickSort(arr, index, right) 98 | } 99 | return arr; 100 | }} 101 | -------------------------------------------------------------------------------- /pyffi-tutorials/test-numpy.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require pyffi #;pyffi/numpy) 4 | 5 | (set-environment-variables) 6 | (initialize) 7 | ;(import-numpy) 8 | (finish-initialization) 9 | -------------------------------------------------------------------------------- /pyffi-tutorials/test-pillow.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require pyffi) 3 | ;;; 4 | ;;; Pillow 5 | ;;; 6 | 7 | ;; The Python library Pillow is the successor of PIL (Python Imaging Library). 8 | ;; The example below uses the "P" mode of `convert` to convert a logo 9 | ;; into a png with a pallette of only 256 colors. 10 | 11 | ;; https://pillow.readthedocs.io/en/stable/index.html 12 | 13 | 14 | ;; Setup Python 15 | 16 | (initialize) 17 | (finish-initialization) 18 | 19 | ;; Import Pillow 20 | 21 | (import PIL) 22 | (import-from PIL Image) ; Imports the submodule `Image` 23 | 24 | ;; Load image 25 | (define im (Image.open "logo.png")) ; has a gradient 26 | 27 | ;; Image Info 28 | (displayln (list im.format im.size im.mode)) 29 | ; Note: The format is only set for images loaded from disk. 30 | ; For images created by the library, the format is set to None. 31 | 32 | ;; Convert to palette mode and save 33 | (define im2 (im.convert "P")) ; the "P" mode uses a pallette [default is 256 colors] 34 | ((im.convert "P") .save "result.png") 35 | (displayln (list im2.size im2.mode)) 36 | 37 | ; (im2.show) ; show the image in default image viewer 38 | -------------------------------------------------------------------------------- /pyffi-tutorials/tutorial-mix-racket-and-python.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | 4 | ;;; 5 | ;;; Mix Racket and Pything 6 | ;;; 7 | 8 | ;; Setup Python 9 | (initialize) 10 | (finish-initialization) 11 | 12 | 13 | (pr (run* "x = 42")) 14 | ;(pr (run "x")) 15 | 16 | -------------------------------------------------------------------------------- /pyffi-tutorials/tutorial-numpy-fish-market.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | ;;; 3 | ;;; Fish Market 4 | ;;; 5 | 6 | ;; This example uses a data set from Finland. 7 | ;; A number of fish were caught and measurements for each fish were recorded. 8 | 9 | ;; http://jse.amstat.org/datasets/fishcatch.txt 10 | ;; https://www.kaggle.com/datasets/aungpyaeap/fish-market 11 | 12 | ;; One way to use the data set: Consider `weight` the dependent variable 13 | ;; and try to predict the `weight` from the other data. 14 | 15 | ;;; Import and initialize Numpy 16 | 17 | (require pyffi pyffi/numpy) 18 | 19 | (initialize) 20 | (import-numpy) 21 | (finish-initialization) 22 | (declare-special-prefix numpy) 23 | 24 | 25 | ;;; 1. Load the data set 26 | 27 | ;; > less fish.csv 28 | ;; Species,Weight,Length1,Length2,Length3,Height,Width 29 | ;; Bream,242,23.2,25.4,30,11.52,4.02 30 | ;; Bream,290,24,26.3,31.2,12.48,4.3056 31 | ;; ... 32 | 33 | ;; Apart from "Species" all values are floating points. 34 | 35 | ;; >>> dtypes 36 | ;; [('Species', 'U'), ('Weight', 'f8'), ('Length1', 'f8'), ('Length2', 'f8'), 37 | ;; ('Length3', 'f8'), ('Height', 'f8'), ('Width', 'f8')] 38 | 39 | ; `dtype` stands for data type 40 | (define dtypes (list #("Species" "U10") ; unicode, 10 characters 41 | #("Weight" "f8") ; floating-point 42 | #("Length1" "f8") 43 | #("Length2" "f8") 44 | #("Length3" "f8") 45 | #("Height" "f8") 46 | #("Width" "f8"))) 47 | 48 | (define data (numpy.genfromtxt "fish.csv" #:skip_header 1 #:delimiter "," #:dtype dtypes)) 49 | 50 | ;;; 2. Get to know the data set 51 | 52 | ; Print a sample of 10 fish 53 | 54 | "Sample of 10 fish" 55 | (define sample (numpy.random.choice data #:size 10)) 56 | sample 57 | 58 | "Total number of fish" 59 | ; (ref (numpy.shape #:a data) 0) 60 | (ref (numpy.shape data) 0) 61 | 62 | "Weights in the sample" 63 | (define sample-weights (ref sample "Weight")) 64 | sample-weights 65 | 66 | "Lengths in sample" 67 | (define sample-lengths (ref sample "Length1")) 68 | sample-lengths 69 | 70 | "Minimal length" 71 | (define sample-min-length (from (numpy.min sample-lengths))) 72 | sample-min-length 73 | 74 | "Maximal length" 75 | (define sample-max-length (from (numpy.max sample-lengths))) 76 | sample-max-length 77 | 78 | ; Could `weight` be estimated using `length1`? 79 | 80 | (require plot) 81 | (plot-new-window? #t) 82 | (plot #:title "Sample plot" #:x-label "length1 (cm)" #:y-label "weight (g)" 83 | (points (list->vector (map vector (from sample-lengths) (from sample-weights))))) 84 | 85 | ; Conclusion for now: to some degree. 86 | 87 | ;;; 3. Transform data for use with Gradient Descent 88 | 89 | (define T numpy.transpose) 90 | (define dot numpy.dot) 91 | 92 | (define (column xs) ; turn an 1d array into a column vector 93 | (T (numpy.atleast_2d xs))) 94 | 95 | "Weigths as column vector" 96 | (column sample-weights) 97 | 98 | 99 | (define (prepend-ones Xs) 100 | (define m (ref (numpy.shape Xs) 0)) 101 | (numpy.hstack (vector (numpy.ones `#(,m 1)) 102 | (column Xs)))) 103 | 104 | "Lengths with a one-column in front" 105 | (prepend-ones sample-lengths) 106 | 107 | 108 | ;;; 4. Find a model for `weight` based on `length1`. 109 | 110 | ;; We will attempt to find a linear model of the form: 111 | ;; weight = w0 + w1 * length1 112 | ;; Our job is to find the weights w0 and w1. 113 | 114 | ;; If we write the model as: 115 | ;; weight = w0*1 + w1 * length1 116 | ;; We see that the weight is the dot products between the weights and 117 | ;; the length with a prepended 1. 118 | ;; weight = w . (1,length) where w is the vector (w0,w1) 119 | 120 | ;; The standard regression method is minimal least squares. 121 | ;; The sum of the squares of the residual must be minimal. 122 | ;; sum_residual_sqares(w) = sum( y - w . xs ) 123 | ;; where y is the observed values (the weights), 124 | ;; and xs are the independent variables with a 1 in front (here the length). 125 | 126 | 127 | "Using Numpys least square solver" 128 | (define sample-solution (numpy.linalg.lstsq (prepend-ones sample-lengths) sample-weights #:rcond (void))) 129 | (define sample-coeffs (ref sample-solution 0)) 130 | (define sample-residuals (ref sample-solution 1)) 131 | (define sample-rank (ref sample-solution 2)) 132 | (define sample-singular-values (ref sample-solution 3)) 133 | "Sample: solution" 134 | (from sample-coeffs) 135 | "Sample: sum of squared residuals" 136 | sample-residuals 137 | "Sample plot with regression line" 138 | (let () 139 | (match-define (list w0 w1) (from sample-coeffs)) 140 | (define (f x) (+ w0 (* w1 x))) 141 | (plot #:title "Sample with linear model" #:x-label "length1 (cm)" #:y-label "weight (g)" 142 | (list (points (list->vector (map vector (from sample-lengths) (from sample-weights)))) 143 | (function f sample-min-length sample-max-length)))) 144 | 145 | ;;; 5. Fine linear model using Gradient Descent 146 | 147 | ;; https://en.wikipedia.org/wiki/Gradient_descent 148 | 149 | ;; The function `sum_residual_squares` can be minimized using the method of Gradient Descent. 150 | ;; srs(w) = sum_residual_sqares(w) = sum( y - w . xs ) 151 | 152 | ;; We already know the result, but this allow us to test the validity of our grafient 153 | ;; descent implementation. 154 | 155 | ;; For a given `w` the gradient of `srs` at `w` is a vector that points in the direction 156 | ;; that maximized the growth of `srs`. Thus minus the gradient is a vector that points 157 | ;; in the direction that minimizes `srs`. 158 | 159 | ;; If we repeatedly subtract the gradient (more precisely: a vector with the same 160 | ;; direction as the gradient) from our guess, the guess will improve. 161 | 162 | ;; The idea of gradient descent is simple: 163 | 164 | ;; w = initial_guess 165 | ;; loop: 166 | ;; compute gradient 167 | ;; w = w - α gradient 168 | 169 | ;; the coefficient α is called the learning rate. 170 | ;; We will use a very simple version of gradient descent and let α be fixed. 171 | 172 | ;; The formula for the gradient of `srs` can be found at Wikipedia. 173 | 174 | ;; def descent(X, y, α = 0.001, iters = 100): 175 | ;; w = np.zeros((X.shape[1], 1)) 176 | ;; for i in range(iters): 177 | ;; gradient = -2.(X.T).dot(y - X.dot(w)) 178 | ;; w = w - α*gradient 179 | ;; return w 180 | 181 | 182 | (define (gradient-descent xss ys [α 0.001] [iterations 100]) 183 | (define X (prepend-ones xss)) 184 | (set! ys (column ys)) 185 | (define n (ref (numpy.shape ys) 1)) ; n = number of features measured (number of columns) 186 | (define w (numpy.zeros (vector (+ n 1) 1))) ; (n+1)x1 187 | 188 | (for ([i (in-range iterations)]) 189 | (define predictions (dot X w)) 190 | (define observed ys) 191 | (define residuals (sub observed predictions)) 192 | (define gradient (mul -2. (dot (T X) residuals))) 193 | (set! w (sub w (mul α gradient)))) 194 | w) 195 | 196 | "Sample solution found with Gradient Descent" 197 | ; these parameters seem to work for most samples 198 | #;(gradient-descent sample-lengths sample-weights 0.0001 100000) 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /pyffi-tutorials/tutorial-pygments.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | (require pyffi) 3 | 4 | ;;; 5 | ;;; PYGMENTS 6 | ;;; 7 | 8 | ;; Pygments is a generic syntax highlighter. 9 | 10 | ;; Example from https://pygments.org/docs/quickstart/ 11 | 12 | 13 | ;; from pygments import highlight 14 | ;; from pygments.lexers import PythonLexer 15 | ;; from pygments.formatters import HtmlFormatter 16 | 17 | ;; code = 'print "Hello World"' 18 | ;; print(highlight(code, PythonLexer(), HtmlFormatter())) 19 | 20 | 21 | ;; Setup Python 22 | (initialize) 23 | (post-initialize) 24 | 25 | ;; Import Pygments 26 | (import-from pygments highlight) 27 | (import-from pygments.lexers PythonLexer) 28 | (import-from pygments.formatters HtmlFormatter) 29 | 30 | ;; Use Pygments 31 | (define code "print 'Hello World'") 32 | (displayln (highlight code (PythonLexer) (HtmlFormatter))) 33 | 34 | ;; Get the stylesheet 35 | ; (displayln ((HtmlFormatter) .get_style_defs ".hightlight")) 36 | 37 | ;; from pygments.lexers import get_all_lexers 38 | ;; (import-from pygments.lexers get_all_lexers) 39 | ;; (define i (get_all_lexers)) 40 | ;; (i.__next__) 41 | -------------------------------------------------------------------------------- /pyffi-tutorials/tutorial-skia.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | ;;; 3 | ;;; SKIA 4 | ;;; 5 | 6 | ;; Skia is an open source 2D graphics library. 7 | ;; It is cross platform and is used in Google Chrome. 8 | ;; It's not unfair to say that Skia a modern version of Cairo 9 | ;; (Cairo is the graphics library used to implement racket/draw). 10 | 11 | ;; Skia is implemented C++ and currently there are no direct bindings 12 | ;; of Skia for Racket. However, we can use the Python bindings for Skia 13 | ;; from Racket. 14 | 15 | (require pyffi) 16 | (initialize) 17 | (post-initialize) 18 | 19 | 20 | (import skia) 21 | (define None (void)) 22 | 23 | 24 | ;;; 25 | ;;; DRAW 26 | ;;; 27 | 28 | ; All examples draw on a canvas and saves the result in a png file. 29 | 30 | (define (draw f filename [width 300] [height 300]) 31 | (define surface (skia.Surface width height)) 32 | (define canvas (surface.__enter__)) 33 | (f canvas) 34 | (surface.__exit__ None None None) 35 | (define image (surface.makeImageSnapshot)) 36 | (image.save filename skia.kPNG)) 37 | 38 | 39 | ;;; 40 | ;;; Circle 41 | ;;; 42 | 43 | (define (circle canvas) 44 | (canvas.drawCircle 100 100 40 (skia.Paint #:Color skia.ColorRED))) 45 | 46 | ;;; 47 | ;;; Drop Shadow 48 | ;;; 49 | 50 | ;; canvas.drawColor(skia.ColorWHITE) 51 | ;; paint = skia.Paint(ImageFilter=skia.ImageFilters.DropShadow(3, 3, 5, 5, skia.ColorBLACK)) 52 | ;; blob = skia.TextBlob("Skia", skia.Font(None, 120)) 53 | ;; canvas.drawTextBlob(blob, 0, 160, paint) 54 | 55 | (define (dropshadow canvas) 56 | (canvas.drawColor skia.ColorWHITE) 57 | (define paint (skia.Paint #:ImageFilter (skia.ImageFilters.DropShadow 3 3 5 5 skia.ColorBLACK))) 58 | (define blob (skia.TextBlob "Skia" (skia.Font None 120))) 59 | (canvas.drawTextBlob blob 0 160 paint)) 60 | 61 | ;;; 62 | ;;; Heptagram 63 | ;;; 64 | 65 | ;; void draw(SkCanvas* canvas) { 66 | ;; const SkScalar scale = 256.0f; 67 | ;; const SkScalar R = 0.45f * scale; 68 | ;; const SkScalar TAU = 6.2831853f; 69 | ;; SkPath path; 70 | ;; path.moveTo(R, 0.0f); 71 | ;; for (int i = 1; i < 7; ++i) { 72 | ;; SkScalar theta = 3 * i * TAU / 7; 73 | ;; path.lineTo(R * cos(theta), R * sin(theta)); 74 | ;; } 75 | ;; path.close(); 76 | ;; SkPaint p; 77 | ;; p.setAntiAlias(true); 78 | ;; canvas->clear(SK_ColorWHITE); 79 | ;; canvas->translate(0.5f * scale, 0.5f * scale); 80 | ;; canvas->drawPath(path, p); 81 | ;; } 82 | 83 | (define (heptagram canvas) 84 | (define scale 256.) 85 | (define R (* 0.45 scale)) 86 | (define τ 6.2831853) 87 | 88 | (define path (skia.Path)) 89 | (path .moveTo R 0.) 90 | (for ([i (in-range 1 7)]) 91 | (define θ (* 3/7 i τ)) 92 | (path .lineTo (* R (cos θ)) (* R (sin θ)))) 93 | (path .close) 94 | 95 | (define p (skia.Paint)) 96 | (p .setAntiAlias #t) 97 | (canvas .drawColor skia.ColorWHITE) 98 | (canvas .translate (* 0.5 scale) (* 0.5 scale)) 99 | (canvas .drawPath path p)) 100 | 101 | 102 | (draw circle "output-circle.png") 103 | (draw dropshadow "output-dropshadow.png") 104 | (draw heptagram "output-heptagram.png") 105 | -------------------------------------------------------------------------------- /pyffi/info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define collection 'multi) 4 | 5 | (define deps '("pyffi-doc" "pyffi-lib")) 6 | (define build-deps '("pyffi-doc" )) ; omittable from a binary package 7 | (define implies '("pyffi-lib")) 8 | 9 | (define pkg-desc "Use Python from Racket.") 10 | 11 | (define pkg-authors '(soegaard)) 12 | 13 | (define test-responsibles '((all jensaxel@soegaard.net))) 14 | 15 | -------------------------------------------------------------------------------- /pyffi/main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require "../pyffi-lib/pyffi/python.rkt") 4 | (provide (all-from-out "../pyffi-lib/pyffi/python.rkt")) 5 | -------------------------------------------------------------------------------- /python-scripts/signature-pyautogui.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import pyautogui 3 | 4 | ### 5 | ### This script finds the functions in `numpy.pyautogui` that have signatures. 6 | ### 7 | 8 | 9 | # Attempt to get the signature for `f`. 10 | # Return `False` when no signature is available. 11 | def sign(f): 12 | try: 13 | return inspect.signature(f) 14 | except: 15 | return False 16 | 17 | mod = pyautogui 18 | 19 | print("Functions where inspect.signature fails") 20 | print("---") 21 | for x in dir(mod): 22 | f = getattr(mod,x) 23 | if inspect.isfunction(f): 24 | if not sign(f): 25 | print(x) 26 | 27 | print("Functions where inspect.signature works") 28 | print("---") 29 | for x in dir(mod): 30 | f = getattr(mod,x) 31 | if inspect.isfunction(f): 32 | if sign(f): 33 | print(x) 34 | 35 | print("") 36 | print("Builtins (where inspect.signature fails)") 37 | print("---") 38 | for x in dir(mod): 39 | f = getattr(mod,x) 40 | if (not inspect.isfunction(f)) and inspect.isbuiltin(f): 41 | if not sign(f): 42 | print(x) 43 | 44 | -------------------------------------------------------------------------------- /python-scripts/signature-random.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import numpy 3 | 4 | ### 5 | ### This script finds the functions in `numpy.random` that have no signatures. 6 | ### 7 | 8 | 9 | # Attempt to get the signature for `f`. 10 | # Return `False` when no signature is available. 11 | def sign(f): 12 | try: 13 | return inspect.signature(f) 14 | except: 15 | return False 16 | 17 | mod = numpy.random 18 | 19 | print("Functions where inspect.signature fails") 20 | print("---") 21 | for x in dir(mod): 22 | f = getattr(mod,x) 23 | if inspect.isfunction(f): 24 | if not sign(f): 25 | print(x) 26 | 27 | print("Functions where inspect.signature works") 28 | print("---") 29 | for x in dir(mod): 30 | f = getattr(mod,x) 31 | if inspect.isfunction(f): 32 | if sign(f): 33 | print(x) 34 | 35 | print("") 36 | print("Builtins (where inspect.signature fails)") 37 | print("---") 38 | for x in dir(mod): 39 | f = getattr(mod,x) 40 | if (not inspect.isfunction(f)) and inspect.isbuiltin(f): 41 | if not sign(f): 42 | print(x) 43 | 44 | -------------------------------------------------------------------------------- /python-scripts/signature.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import numpy 3 | 4 | # Attempt to get the signature for `f`. 5 | # Return `False` when no signature is available. 6 | def sign(f): 7 | try: 8 | return inspect.signature(f) 9 | except: 10 | return False 11 | 12 | mod = numpy.random 13 | 14 | print("Numpy functions where inspect.signature fails") 15 | print("---") 16 | for x in dir(mod): 17 | f = getattr(mod,x) 18 | if inspect.isfunction(f): 19 | if not sign(f): 20 | print(x) 21 | 22 | print("") 23 | print("Numpy functions where inspect.signature works") 24 | print("---") 25 | for x in dir(mod): 26 | f = getattr(mod,x) 27 | if inspect.isfunction(f): 28 | if sign(f): 29 | print(x) 30 | 31 | print("") 32 | print("Non functions") 33 | print("---") 34 | print("---") 35 | 36 | print("Ufuncs") 37 | print("---") 38 | 39 | for x in dir(mod): 40 | f = getattr(mod,x) 41 | if not inspect.isfunction(f): 42 | if type(f)==type(numpy.absolute): # a 43 | print(x) 44 | 45 | print("") 46 | print("Builtins") 47 | print("---") 48 | for x in dir(mod): 49 | f = getattr(mod,x) 50 | if (not inspect.isfunction(f)) and inspect.isbuiltin(f): 51 | print(x) 52 | 53 | print("") 54 | print("Non functions besides ufuncs and builtins") 55 | print("---") 56 | for x in dir(mod): 57 | f = getattr(mod,x) 58 | if not inspect.isfunction(f): 59 | if not type(f)==type(numpy.absolute): # a 60 | if not inspect.isbuiltin(f): 61 | print(x) 62 | --------------------------------------------------------------------------------