From 8bd2b9155f93845f4b52bcb0890991ef75087bd4 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 27 Jan 2025 11:22:31 -0500 Subject: [PATCH 1/8] Minor edits to the documentation --- hyrule/control.hy | 17 ++++++++++------- hyrule/destructure.hy | 2 +- hyrule/iterables.hy | 10 +++++----- hyrule/macrotools.hy | 12 ++++++------ hyrule/oop.hy | 2 +- hyrule/sequences.hy | 2 +- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/hyrule/control.hy b/hyrule/control.hy index 8ec2695a..0c8fa2f4 100644 --- a/hyrule/control.hy +++ b/hyrule/control.hy @@ -179,18 +179,18 @@ ``sys.argv`` is always the name of the script being invoked, whereas the rest are command-line arguments. If ``args`` is ``[]``, this will be treated like ``[#* _]``, so any command-line arguments (and the script name) will be - allowed, but ignored. + allowed, but ignored. :: + + (defmain [program-name argument] + (print "Welcome to" program-name) + (print "The answer is" (* (float argument) 2))) If the defined function returns an :class:`int`, :func:`sys.exit` is called with that integer as the return code. If you want fancy command-line arguments, you can use the standard Python module :mod:`argparse` in the usual way, because ``defmain`` doesn't change - ``sys.argv``. See also :hy:func:`parse-args`. :: - - (defmain [program-name argument] - (print "Welcome to" program-name) - (print "The answer is" (* (float argument) 2)))]] + ``sys.argv``. See also :hy:func:`parse-args`.]] (setv retval (hy.gensym) restval (hy.gensym)) @@ -264,13 +264,16 @@ With ``loop``, this would be written as:: + (require hyrule [loop]) + (import hyrule [recur]) + (defn factorial [n] (loop [[n n] [acc 1]] (if n (recur (- n 1) (* acc n)) acc))) - Don't forget to ``(import hyrule [recur])``. The :hy:class:`recur` object holds the arguments for the next call. When the function returns a :hy:class:`recur`, ``loop`` calls it again with the new arguments. Otherwise, ``loop`` ends and the final value is returned. Thus, what would be a nested set of recursive calls becomes a series of calls that are resolved entirely in sequence. + The :hy:class:`recur` object holds the arguments for the next call. When the function returns a :hy:class:`recur`, ``loop`` calls it again with the new arguments. Otherwise, ``loop`` ends and the final value is returned. Thus, what would be a nested set of recursive calls becomes a series of calls that are resolved entirely in sequence. Note that while ``loop`` uses the same syntax as ordinary function definitions for its lambda list, all parameters other than ``#* args`` and ``#** kwargs`` must have a default value, because the function will first be called with no arguments." diff --git a/hyrule/destructure.hy b/hyrule/destructure.hy index 0a971af4..c3726b39 100644 --- a/hyrule/destructure.hy +++ b/hyrule/destructure.hy @@ -345,7 +345,7 @@ Take pairs of destructuring patterns and input data structures, and return a dic s))))) (defmacro defn+ [#* args] - "As :hy:func:`defn`, but the lambda list is destructured as a list pattern. The usual special parameter names in lambda lists, such as `#*`, aren't special here. No type annotations are allowed are in the lambda list, but a return-value annotation for the whole function is allowed." + "As :hy:func:`defn`, but the lambda list is destructured as a list pattern. The usual special parameter names in lambda lists, such as ``#*``, aren't special here. No type annotations are allowed are in the lambda list, but a return-value annotation for the whole function is allowed." (destructuring-fn 'defn args)) (defmacro fn+ [#* args] diff --git a/hyrule/iterables.hy b/hyrule/iterables.hy index 7d1ade3c..a60f3732 100644 --- a/hyrule/iterables.hy +++ b/hyrule/iterables.hy @@ -15,7 +15,7 @@ (defn coll? [x] #[[Return ``True`` if ``x`` inherits from :class:`collections.abc.Iterable` - but not ``str`` or ``bytes``. :: + but not :class:`str` or :class:`bytes`. :: (coll? ["abc"]) ; True (coll? {"a" 1 "b" 2}) ; True @@ -29,7 +29,7 @@ (defn distinct [coll] "Return an iterator from the original iterable ``coll`` with no duplicates. Duplicates are detected by calling :hy:func:`in - ` on a :class:`set`. Elements will be produced in order + ` on a :class:`set`. Elements are produced in order of their first appearance in ``coll``. :: (list (distinct [1 2 3 4 3 5 0 2])) ; => [1 2 3 4 5 0]" @@ -95,11 +95,11 @@ arguments as ``range``, but includes the endpoint (given a compatible start point and step size). :: - (thru 3) + (list (thru 3)) ; => [0 1 2 3] - (thru 0 10 2) + (list (thru 0 10 2)) ; => [0 2 4 6 8 10] - (thru 0 9 2) + (list (thru 0 9 2)) ; => [0 2 4 6 8]" (when (is b None) diff --git a/hyrule/macrotools.hy b/hyrule/macrotools.hy index 3268ad0e..77b185e4 100644 --- a/hyrule/macrotools.hy +++ b/hyrule/macrotools.hy @@ -45,11 +45,11 @@ (defn match-fn-params [args params] #[[Match an iterable of arguments against a parameter list in the - style of a :hy:func:`defn` lambda list. The parameter-list syntax - here is somewhat restricted: annotations are forbiddden, ``/`` and - ``*`` aren't recognized, and nothing is allowed after ``#* args`` - other than ``#** kwargs``. Return a dictionary of the parameters and - their values. :: + style of a :hy:func:`defn` lambda list. Return a dictionary of the + parameters and their values. The parameter-list syntax here is + somewhat restricted: annotations are forbiddden, ``/`` and ``*`` + aren't recognized, and nothing is allowed after ``#* args`` other + than ``#** kwargs``. :: (match-fn-params [1 :foo "x"] @@ -298,7 +298,7 @@ (+= FOO (ABS -5))) (print foo) ; => 20 - That's why the parameters of ``map-model`` are backwards compared to ``map``: in user code, ``x`` is typically a symbol or other simple form whereas ``f`` is a multi-line anonymous function.]] + That's why the parameters of ``map-model`` are backwards compared to :func:`map`: in user code, ``x`` is typically a symbol or other simple form whereas ``f`` is a multi-line anonymous function.]] (_map-model (hy.as-model x) f)) diff --git a/hyrule/oop.hy b/hyrule/oop.hy index 2d226e83..fefe2b95 100644 --- a/hyrule/oop.hy +++ b/hyrule/oop.hy @@ -3,7 +3,7 @@ (defmacro meth [#* args] - #[[A replacement for :hy:func:`defn` that provides syntactic sugar for ``self``. As the name suggests, it's most useful for defining methods. The parameter list is automatically prepended with ``self``, and any reference to a symbol beginning with ``@``, such as ``@foo``, is replaced by ``self.foo``:: + #[[A replacement for :hy:func:`defn` that provides syntactic sugar for ``self``. As the name suggests, it's most useful for defining methods. The parameter list is automatically prepended with ``self``, and any reference to a symbol beginning with ``@`` is replaced such that ``@foo`` becomes ``self.foo``:: (defclass BirdWatcher [] diff --git a/hyrule/sequences.hy b/hyrule/sequences.hy index 26243d6b..781385d1 100644 --- a/hyrule/sequences.hy +++ b/hyrule/sequences.hy @@ -64,7 +64,7 @@ (if (> (len items) self.max-items-in-repr) ", ..." "")))) (defmacro seq [param #* seq-code] - "Define a :hy:class:`Sequence` with code to compute the *n*-th element, where *n* starts from 0. The first argument is a literal list with a symbol naming the parameter, like the lambda list of :hy:func:`defn`, and the other arguments are the code to be evaluated. :: + r"Define a :hy:class:`Sequence` with code to compute the ``n``\th element, where ``n`` starts at 0. The first argument is a literal list with a symbol naming the parameter, like the lambda list of :hy:func:`defn`, and the other arguments are the code to be evaluated. :: (setv s (seq [n] (** n 2))) (print (get s 2)) ; => 4 From d77dd1be25b67c2413a40c53cce987b4abecd84e Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Mon, 27 Jan 2025 11:27:34 -0500 Subject: [PATCH 2/8] Use more logical gensym names in `import-path` --- NEWS.rst | 7 +++++++ hyrule/misc.hy | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index d5cc914c..d031d2d2 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -8,6 +8,13 @@ Removals * `defn/a+` has been removed. Use `(defn+ :async …)` instead. * `fn/a+` has been removed. Use `(fn+ :async …)` instead. +Other Breaking Changes +------------------------------ +* The names of gensyms produced by `import-path` have changed. (This + shouldn't break your code unless you're doing something + exceptionally weird, but the argument to `hy.gensym` was documented, + so this is technically a breaking change.) + Bug Fixes ------------------------------ * A lot of incompatibilities of `meth`, `ameth`, `defn+`, and `fn+` diff --git a/hyrule/misc.hy b/hyrule/misc.hy index fd8ef092..9285048d 100644 --- a/hyrule/misc.hy +++ b/hyrule/misc.hy @@ -61,7 +61,7 @@ #[[Import the Python or Hy source code at ``path`` as a module with :func:`importlib.util.spec_from_file_location`, per Python's documentation. Return the new module object. ``name`` defaults to ``(str (hy.gensym - "import-file"))``. :: + "import-path"))``. :: (setv p (hy.I.pathlib.Path "mymodule.hy")) (.write-text p "(setv foo 3)") @@ -69,7 +69,7 @@ (print m.foo) ; => 3]] (when (is name None) - (setv name (str (hy.gensym "import-file")))) + (setv name (str (hy.gensym "import-path")))) (when (in name sys.modules) (raise (ValueError f"The name {(hy.repr name)} is already in use in `sys.modules`."))) From 01a649e827b7740f1f0d1496c23c89bde1f0fdca Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 10:48:36 -0500 Subject: [PATCH 3/8] Replace `with-gensyms` with `def-gensyms` The implicit `do` has no function. It just makes it look like the bindings are locally scoped to the implicit `do`, when in fact, they aren't. The name `defgensyms` would be more consistent with `defn` and `defmacro`, but I felt it had a little too much clustering of letters. --- NEWS.rst | 1 + docs/index.rst | 2 +- hyrule/macrotools.hy | 30 +++++++++++++----------------- tests/test_macrotools.hy | 10 +++++----- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index d031d2d2..1285c0b7 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -10,6 +10,7 @@ Removals Other Breaking Changes ------------------------------ +* `(with-gensyms [a b c] …)` is now written `(def-gensyms a b c) …`. * The names of gensyms produced by `import-path` have changed. (This shouldn't break your code unless you're doing something exceptionally weird, but the argument to `hy.gensym` was documented, diff --git a/docs/index.rst b/docs/index.rst index de6013ba..5e6d0a18 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -97,13 +97,13 @@ API .. hy:automodule:: hyrule.macrotools .. hy:autotag:: / +.. hy:automacro:: def-gensyms .. hy:automacro:: defmacro-kwargs .. hy:automacro:: defmacro! .. hy:autofunction:: macroexpand-all .. hy:autofunction:: map-hyseq .. hy:autofunction:: map-model .. hy:autofunction:: match-fn-params -.. hy:automacro:: with-gensyms ``oop`` — Tools for object-oriented programming ---------------------------------------------------------------------- diff --git a/hyrule/macrotools.hy b/hyrule/macrotools.hy index 77b185e4..15127eef 100644 --- a/hyrule/macrotools.hy +++ b/hyrule/macrotools.hy @@ -333,28 +333,24 @@ x)) -(defmacro with-gensyms [args #* body] +(defmacro def-gensyms [#* symbols] - #[[Evaluate ``body`` with each name in ``args`` (a list of symbols) bound to - a gensym. The syntax :: + #[[Define a number of gensyms, binding each symbol to a call to + :hy:func:`hy.gensym`. The syntax :: - (with-gensyms [a b c] - …) + (def-gensyms a b c) is equivalent to :: - (do - (setv a (hy.gensym 'a)) - (setv b (hy.gensym 'b)) - (setv c (hy.gensym 'c)) - …)]] - - (setv syms []) - (for [arg args] - (.extend syms [arg `(hy.gensym '~arg)])) - `(do - (setv ~@syms) - ~@body)) + (setv + a (hy.gensym 'a) + b (hy.gensym 'b) + c (hy.gensym 'c))]] + + `(setv ~@(gfor + sym symbols + x [sym `(hy.gensym '~sym)] + x))) (defreader / diff --git a/tests/test_macrotools.hy b/tests/test_macrotools.hy index c42aa968..4371ed03 100644 --- a/tests/test_macrotools.hy +++ b/tests/test_macrotools.hy @@ -1,5 +1,5 @@ (require - hyrule [defmacro-kwargs defmacro! with-gensyms ->] + hyrule [defmacro-kwargs defmacro! def-gensyms ->] :readers [/]) (import pytest @@ -29,9 +29,9 @@ (assert (do-mac x-is-plain-int?))) -(defmacro example--with-gensyms [] - (with-gensyms [a] - `(setv ~a 1))) +(defmacro example--def-gensyms [] + (def-gensyms a) + `(setv ~a 1)) (defmacro! example--defmacro! [] `(setv ~g!res 1)) @@ -42,7 +42,7 @@ (len (sfor a (dir C) :if (not (.startswith a "__")) a)) 2))) - (defclass C [] (example--with-gensyms) (example--with-gensyms)) + (defclass C [] (example--def-gensyms) (example--def-gensyms)) (check) (defclass C [] (example--defmacro!) (example--defmacro!)) (check)) From 73a3db9ebbe1f10409894278c58855260ae61a2b Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 10:49:40 -0500 Subject: [PATCH 4/8] Move `def-gensyms` --- hyrule/macrotools.hy | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/hyrule/macrotools.hy b/hyrule/macrotools.hy index 15127eef..1d2ad112 100644 --- a/hyrule/macrotools.hy +++ b/hyrule/macrotools.hy @@ -4,6 +4,26 @@ hyrule.iterables [coll? flatten]) +(defmacro def-gensyms [#* symbols] + + #[[Define a number of gensyms, binding each symbol to a call to + :hy:func:`hy.gensym`. The syntax :: + + (def-gensyms a b c) + + is equivalent to :: + + (setv + a (hy.gensym 'a) + b (hy.gensym 'b) + c (hy.gensym 'c))]] + + `(setv ~@(gfor + sym symbols + x [sym `(hy.gensym '~sym)] + x))) + + (defmacro defmacro-kwargs [name params #* body] #[=[Define a macro that can take keyword arguments. When the macro @@ -333,26 +353,6 @@ x)) -(defmacro def-gensyms [#* symbols] - - #[[Define a number of gensyms, binding each symbol to a call to - :hy:func:`hy.gensym`. The syntax :: - - (def-gensyms a b c) - - is equivalent to :: - - (setv - a (hy.gensym 'a) - b (hy.gensym 'b) - c (hy.gensym 'c))]] - - `(setv ~@(gfor - sym symbols - x [sym `(hy.gensym '~sym)] - x))) - - (defreader / #[[Read one identifier and interpret it as a one-shot import in the same From 3dbacbee5058d9d5e4780486577649925c19f55b Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 10:47:57 -0500 Subject: [PATCH 5/8] Use `def-gensyms` in more places interally --- hyrule/anaphoric.hy | 4 ++-- hyrule/argmove.hy | 6 ++++-- hyrule/control.hy | 11 +++++------ hyrule/destructure.hy | 17 ++++++++--------- hyrule/macrotools.hy | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/hyrule/anaphoric.hy b/hyrule/anaphoric.hy index 54de00a9..52037b05 100644 --- a/hyrule/anaphoric.hy +++ b/hyrule/anaphoric.hy @@ -4,7 +4,7 @@ easier to read. An anaphoric macro assigns values to designated symbols (require - hyrule.macrotools [defmacro!] + hyrule.macrotools [defmacro! def-gensyms] hyrule.argmove [->]) @@ -100,7 +100,7 @@ easier to read. An anaphoric macro assigns values to designated symbols there is no such element. :: (ap-last (> it 5) (range 10)) ; => 9" - (setv x (hy.gensym)) + (def-gensyms x) `(let [it None] (setv ~x None) (for [it ~xs :if ~form] diff --git a/hyrule/argmove.hy b/hyrule/argmove.hy index 94f55fbc..f701de34 100644 --- a/hyrule/argmove.hy +++ b/hyrule/argmove.hy @@ -2,6 +2,8 @@ also known as arrow macros." +(require + hyrule.macrotools [def-gensyms]) (import hyrule.iterables [rest] itertools [chain]) @@ -116,7 +118,7 @@ also known as arrow macros." (.get {"a" 1 "b" 2} char)) (some-> "q" lookup (print "is the value")) ; Prints nothing, since `(lookup "q")` returns `None`.]] - (setv val (hy.gensym)) + (def-gensyms val) `(cond (is (setx ~val ~head) None) None ~@(chain.from_iterable (gfor node args [`(is (setx ~val (hy.R.hyrule.-> ~val ~node)) None) None])) @@ -144,7 +146,7 @@ also known as arrow macros." (doto [] (.append 1) (.append 2) (.reverse)) ; => [2 1]" - (setv f (hy.gensym)) + (def-gensyms f) (defn build-form [expression] (setv expression (_dotted expression)) (if (isinstance expression hy.models.Expression) diff --git a/hyrule/control.hy b/hyrule/control.hy index 0c8fa2f4..030fa7ac 100644 --- a/hyrule/control.hy +++ b/hyrule/control.hy @@ -1,5 +1,5 @@ (require - hyrule.macrotools [defmacro!]) + hyrule.macrotools [defmacro! def-gensyms]) (import hyrule.collections [by2s] hyrule.iterables [coll?] @@ -123,7 +123,7 @@ ; the key exactly once. (when (% (len rest) 2) (raise (TypeError "each test-form needs a result-form"))) - (setv x (hy.gensym "case-key")) + (def-gensyms x) `(do (setv ~x ~key) (cond ~@(sum @@ -147,7 +147,7 @@ (defn _do-n [count-form body] - (setv count (hy.gensym)) + (def-gensyms count) `(do (setv ~count ~count-form) (for [~(hy.gensym) @@ -192,8 +192,7 @@ module :mod:`argparse` in the usual way, because ``defmain`` doesn't change ``sys.argv``. See also :hy:func:`parse-args`.]] - (setv retval (hy.gensym) - restval (hy.gensym)) + (def-gensyms retval restval) `(when (= __name__ "__main__") (import sys) (setv ~retval ((fn [~@(or args `[#* ~restval])] ~@body) #* sys.argv)) @@ -247,7 +246,7 @@ (setv counter 0) (list-n 5 (+= counter 1) counter) ; => [1 2 3 4 5]" - (setv l (hy.gensym)) + (def-gensyms l) `(do (setv ~l []) ~(_do-n count-form [`(.append ~l (do ~@body))]) diff --git a/hyrule/destructure.hy b/hyrule/destructure.hy index c3726b39..fe09accb 100644 --- a/hyrule/destructure.hy +++ b/hyrule/destructure.hy @@ -126,7 +126,7 @@ Iterator patterns are specified with a :class:`hy.models.Expression`. They work (require hyrule.argmove [->>] hyrule.control [unless branch] - hyrule.macrotools [defmacro!]) + hyrule.macrotools [defmacro! def-gensyms]) (import itertools [starmap chain count] functools [reduce] @@ -158,8 +158,8 @@ Take pairs of destructuring patterns and input data structures, and return a dic {"apple" 1 "banana" 2}) ; => {'a 1 'b 2}]] - (setv gsyms [] - result (hy.gensym 'dict=:)) + (setv gsyms []) + (def-gensyms result) `(do (setv ~result {} ~@(gfor [binds expr] (by2s pairs) @@ -184,8 +184,8 @@ Take pairs of destructuring patterns and input data structures, and return a dic and :as must be last, if present. " (defn dispatch [f] - (setv dcoll (hy.gensym f.__name__) - result [dcoll expr] + (def-gensyms dcoll) + (setv result [dcoll expr] seen #{}) (defn found [magic target] (when (= magic target) @@ -320,9 +320,8 @@ Take pairs of destructuring patterns and input data structures, and return a dic ``itertools.tee`` to ``foo``. For example, try ``(destructure '(a b c :& more :as it) (count))``. " - (setv [bs magics] (find-magics binds) - copy-iter (hy.gensym) - tee (hy.gensym)) + (setv [bs magics] (find-magics binds)) + (def-gensyms copy-iter tee) (if (in ':as (sfor [x #* _] magics x)) (.extend result [diter `(do (import itertools [tee :as ~tee]) @@ -354,7 +353,7 @@ Take pairs of destructuring patterns and input data structures, and return a dic (defn destructuring-fn [like args] (setv [headers params doc body] (parse-defn-like like args)) - (setv args (hy.gensym) kwargs (hy.gensym)) + (def-gensyms args kwargs) `(~@headers [#* ~args #** ~kwargs] ~doc ~(_expanded-setv params args kwargs) diff --git a/hyrule/macrotools.hy b/hyrule/macrotools.hy index 1d2ad112..57d41116 100644 --- a/hyrule/macrotools.hy +++ b/hyrule/macrotools.hy @@ -32,7 +32,7 @@ local variables that can be used in the macro body. :: (defmacro-kwargs do10times [form [print-iteration 'False]] - (setv i (hy.gensym)) + (def-gensyms i) `(for [~i (range 10)] (when ~print-iteration (print "Now on iteration:" ~i)) @@ -51,7 +51,7 @@ (setv docstring None) (when (and body (isinstance (get body 0) hy.models.String)) (setv [docstring #* body] body)) - (setv g (hy.gensym)) + (def-gensyms g) `(defmacro ~name [#* ~g] ~@(if docstring [docstring] []) (setv ~g (hy.I.hyrule.match-fn-params ~g '~params)) From 6d24a21ff54cd75ab126b802b645ec1cd788cd20 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 11:26:44 -0500 Subject: [PATCH 6/8] Remove `profile/calls` It seems too trivial for code that you wouldn't use often. It's never been tested, I suspect it's unused, and `pycallgraph` is abandoned (the last release was in 2013). --- NEWS.rst | 2 ++ docs/index.rst | 1 - hyrule/misc.hy | 18 ------------------ 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 1285c0b7..9844a268 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -7,6 +7,8 @@ Removals ------------------------------ * `defn/a+` has been removed. Use `(defn+ :async …)` instead. * `fn/a+` has been removed. Use `(fn+ :async …)` instead. +* `profile/calls` has been removed. Use `pycallgraph2` or + `python-call-graph`, which are available on PyPI, instead. Other Breaking Changes ------------------------------ diff --git a/docs/index.rst b/docs/index.rst index 5e6d0a18..db572382 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -144,7 +144,6 @@ API .. hy:autofunction:: import-path .. hy:automacro:: of .. hy:autofunction:: parse-args -.. hy:automacro:: profile/calls .. hy:automacro:: profile/cpu .. hy:automacro:: pun .. hy:autofunction:: sign diff --git a/hyrule/misc.hy b/hyrule/misc.hy index 9285048d..65a80ca3 100644 --- a/hyrule/misc.hy +++ b/hyrule/misc.hy @@ -137,24 +137,6 @@ (.parse-args parser args)) -(defmacro profile/calls [#* body] - "``profile/calls`` allows you to create a call graph visualization. - **Note:** You must have `Graphviz `_ - installed for this to work. - - Examples: - :: - - => (require hyrule.contrib.profile [profile/calls]) - => (profile/calls (print \"hey there\")) - " - `(do - (import pycallgraph [PyCallGraph] - pycallgraph.output [GraphvizOutput]) - (with [(PyCallGraph :output (GraphvizOutput))] - ~@body))) - - (defmacro! profile/cpu [#* body] "Profile a bit of code From 03e45fc7b955f64143fb67869ce59051a04cd394 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 11:28:46 -0500 Subject: [PATCH 7/8] Remove `profile/cpu` Python (since 3.8) has allowed using an instance of `cProfile.Profile` as a context manager, which is basically the same thing as `profile/cpu`, minus all the undocumented and seemingly unmotivated stats stuff. Also, `profile/cpu` was never tested. --- NEWS.rst | 2 ++ docs/index.rst | 1 - hyrule/misc.hy | 37 ------------------------------------- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 9844a268..de0861be 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -9,6 +9,8 @@ Removals * `fn/a+` has been removed. Use `(fn+ :async …)` instead. * `profile/calls` has been removed. Use `pycallgraph2` or `python-call-graph`, which are available on PyPI, instead. +* `profile/cpu` has been removed. Use `(with [pr (cProfile.Profile)] + …)` instead. Other Breaking Changes ------------------------------ diff --git a/docs/index.rst b/docs/index.rst index db572382..59c5a3ad 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -144,7 +144,6 @@ API .. hy:autofunction:: import-path .. hy:automacro:: of .. hy:autofunction:: parse-args -.. hy:automacro:: profile/cpu .. hy:automacro:: pun .. hy:autofunction:: sign .. hy:automacro:: smacrolet diff --git a/hyrule/misc.hy b/hyrule/misc.hy index 65a80ca3..b955bd7f 100644 --- a/hyrule/misc.hy +++ b/hyrule/misc.hy @@ -137,43 +137,6 @@ (.parse-args parser args)) -(defmacro! profile/cpu [#* body] - "Profile a bit of code - - Examples: - :: - - => (require hyrule.contrib.profile [profile/cpu]) - => (profile/cpu (print \"hey there\")) - - .. code-block:: bash - - hey there - - 2 function calls in 0.000 seconds - - Random listing order was used - - ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} - 1 0.000 0.000 0.000 0.000 {print} - - " - `(do - (import cProfile pstats) - - (import io [StringIO]) - - (setv ~g!hy-pr (.Profile cProfile)) - (.enable ~g!hy-pr) - (do ~@body) - (.disable ~g!hy-pr) - (setv ~g!hy-s (StringIO)) - (setv ~g!hy-ps - (.sort-stats (pstats.Stats ~g!hy-pr :stream ~g!hy-s))) - (.print-stats ~g!hy-ps) - (print (.getvalue ~g!hy-s)))) - - (defmacro pun [#* body] #[[Evaluate ``body`` with a shorthand for keyword arguments that are set to variables of the same name. Any keyword whose name starts with an exclamation point, such as ``:!foo``, is replaced with a keyword followed by a symbol, such as ``:foo foo``:: From 091a10b326a1d82a2c5d0101c244f8baba3d91c8 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 30 Jan 2025 12:14:34 -0500 Subject: [PATCH 8/8] Overhaul the docstring and tests for `smacrolet` The previous example with a function's local variable has an unobservable effect. --- hyrule/misc.hy | 35 +++++++++++++++-------------------- tests/test_misc.hy | 26 +++++++++++++++++--------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/hyrule/misc.hy b/hyrule/misc.hy index b955bd7f..c3173ccd 100644 --- a/hyrule/misc.hy +++ b/hyrule/misc.hy @@ -198,30 +198,25 @@ (or a b))) (defmacro smacrolet [_hy_compiler bindings #* body] - "symbol macro let. + #[=[Tell the Hy compiler to translate certain symbols when compiling the body. The effect is similar to symbol macros (as seen in e.g. Common Lisp) and uses the same scoping logic as :hy:func:`let`, hence the name ``smacrolet``, i.e., "symbol macro let". The first argument is a list of bindings, which must be pairs of symbols. :: - Replaces symbols in body, but only where it would be a valid let binding. The - bindings pairs the target symbol and the expansion form for that symbol + (setv x "a") + (setv y "other") + (smacrolet [y x z x] + (+= y "b") + (+= z "c")) + (print x) ; "abc" + (print y) ; "other" - Examples: + The translation doesn't occur in uses of the symbol that wouldn't apply to a :hy:func:`let` binding. Here, for example, ``a`` in an attribute assignment isn't replaced:: - :: + (setv x 1) + (smacrolet [a x] + (defclass C [] + (setv a 2)) + (print a)) ; 1 + (print C.a) ; 2]=] - (smacrolet [b c] - (defn foo [a [b 1]] - (* b (+ a 1))) - (* b (foo 7))) - - Would compile to:: - - (defn foo [a [b 1]] - (* b (+ a 1))) - (* c (foo 7)) - - Notice that the ``b`` symbol defined by the ``defn`` remains unchanged as it - is not a valid ``let`` binding. Only the top level ``b`` sym has been - replaced with ``c`` - " (when (% (len bindings) 2) (raise (ValueError "bindings must be paired"))) diff --git a/tests/test_misc.hy b/tests/test_misc.hy index 6c5e07c8..f8263020 100644 --- a/tests/test_misc.hy +++ b/tests/test_misc.hy @@ -120,17 +120,25 @@ (with [exc (pytest.raises UnboundLocalError)] (smacrolet [b c] b)) - (assert (or (in "cannot access local variable 'c' where it is not associated with a value" (str exc)) - (in "local variable 'c' referenced before assignment" (str exc)))) + (assert (in "local variable 'c'" (. exc value args [0]))) (assert (not-in "b" (locals))) (setv c 42) - (assert (= 42 (smacrolet [b c] b))) + (assert (= (smacrolet [b c] b) 42)) - (smacrolet [b c] - (defn afunc [a [b 1]] (+ a b))) - (assert (= 2 (afunc 1))) + (setv x "a") + (setv y "other") + (smacrolet [y x z x] + (+= y "b") + (+= z "c")) + (assert (= x "abc")) + (assert (= y "other")) - (smacrolet [foo bar] - (setv foo (fn [x] x))) - (assert (= 1 (bar 1)))) + (setv x 1) + (assert (= + (smacrolet [a x] + (defclass C [] + (setv a 2)) + a) + 1)) + (assert (= C.a 2)))