├── .github
├── FUNDING.yml
└── workflows
│ ├── codeql-analysis.yml
│ └── pytest.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Pipfile
├── Pipfile.lock
├── README.md
├── demos
├── example.ipynb
└── sandbox.zsh.ipynb
├── docs
├── build-install-publish.md
├── readme.md
├── reference.md
└── todo.md
├── pyproject.toml
├── screenshots
├── completion.png
├── console.png
├── example.png
└── inspection.png
├── tests
├── __init__.py
├── fun_test.py
├── msgspec_v5.py
├── pexpect_zsh_test.py
└── zsh_kernel_test.py
├── tools
├── Dockerfile
├── docker.zsh
├── install-pip.sh
├── install-pipenv.sh
├── jupyter-rm-zsh.zsh
├── run-lab.sh
└── tag-git.zsh
└── zsh_jupyter_kernel
├── __init__.py
├── __main__.py
├── banner.txt
├── capture.zsh
├── config.py
├── fun.py
├── install.py
├── kernel.py
├── logo-32x32.png
├── logo-64x64.png
├── logo.png
└── version.txt
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: danoak
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ "master" ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ "master" ]
20 | schedule:
21 | - cron: '45 18 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'python' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 |
52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53 | # queries: security-extended,security-and-quality
54 |
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63 |
64 | # If the Autobuild fails above, remove it and uncomment the following three lines.
65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
66 |
67 | # - run: |
68 | # echo "Run, Build Application using script"
69 | # ./location_of_script_within_repo/buildscript.sh
70 |
71 | - name: Perform CodeQL Analysis
72 | uses: github/codeql-action/analyze@v2
73 |
--------------------------------------------------------------------------------
/.github/workflows/pytest.yml:
--------------------------------------------------------------------------------
1 | name: zsh jupyter kernel test
2 |
3 | on: [ push ]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | matrix:
10 | python-version: [ "3.10" ]
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: set up python ${{ matrix.python-version }}
14 | uses: actions/setup-python@v4
15 | with:
16 | python-version: ${{ matrix.python-version }}
17 | - run: sudo apt-get install zsh
18 | - name: install pipenv
19 | run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
20 | - run: pipenv install --dev
21 | - name: install zsh jupyter kernel
22 | run: pipenv run python -m zsh_jupyter_kernel.install --sys-prefix
23 | - name: test with pytest
24 | run: pipenv run python -m pytest
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/
2 |
3 | /build/
4 | /dist/
5 |
6 | .env
7 | /.venv/
8 |
9 | .ipynb_checkpoints/
10 |
11 | .pytest_cache/
12 | __pycache__
13 | *.egg-info
14 |
15 | .idea/
16 | *.iml
17 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # changelog
2 |
3 | ## [3.5.1] - 2024-12-31
4 | ### fixed
5 | - issues with logo copying
6 | - various minor fixes
7 |
8 | ## [3.5.0] - 2022-07-25
9 | ### added
10 | - installation options to change display name and improved options help
11 |
12 | ## [3.4.1] - 2022-07-22
13 | ### removed
14 | - removed logging for production build. this fixes failed installation when log files and directory cannot be created because of access permissions
15 |
16 | ## [3.4] - 2021-07-19
17 | ### fixed
18 | - running `set` command which was previously stuck on execution.
19 | - fixed an error when installing without specifying a `prefix`
20 | - fixed a command to retrieve man pages
21 |
22 | ## [3.2] - 2019-09-05
23 | ### fixed
24 | - fixed issue when zsh did not initialize if host used `add-zsh-hook` to
25 | customize prompts.
26 | - fixed issue when the kernel was timing out when a command did not
27 | responded within 5 seconds.
28 | - fixed issue when the kernel was hanging on continuation prompt
29 | (incomplete input).
30 |
31 | ## [3.0] - 2019-07-07
32 | ### changed
33 | - now package is named `zsh-jupyter-kernel` for specificity
34 | and released at https://pypi.org/project/zsh-jupyter-kernel/
35 |
36 | ## [2.3] - 2019-06-30
37 | ### added
38 | - ⏹ kernel interruption
39 |
40 | ## [2.0] - 2019-06-29
41 | ### added
42 | - multiline support
43 | - this baby is on pypi now: https://pypi.org/project/zsh-kernel/
44 |
45 | ## [1.0] - 2019-06-25
46 | ### added
47 | - required basic kernel functionality for singe line z shell commands.
48 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | [keep a changelog](https://keepachangelog.com).
2 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # GNU GENERAL PUBLIC LICENSE
2 |
3 | Version 3, 29 June 2007
4 |
5 | Copyright (C) 2007 Free Software Foundation, Inc.
6 |
7 |
8 | Everyone is permitted to copy and distribute verbatim copies of this
9 | license document, but changing it is not allowed.
10 |
11 | ## Preamble
12 |
13 | The GNU General Public License is a free, copyleft license for
14 | software and other kinds of works.
15 |
16 | The licenses for most software and other practical works are designed
17 | to take away your freedom to share and change the works. By contrast,
18 | the GNU General Public License is intended to guarantee your freedom
19 | to share and change all versions of a program--to make sure it remains
20 | free software for all its users. We, the Free Software Foundation, use
21 | the GNU General Public License for most of our software; it applies
22 | also to any other work released this way by its authors. You can apply
23 | it to your programs, too.
24 |
25 | When we speak of free software, we are referring to freedom, not
26 | price. Our General Public Licenses are designed to make sure that you
27 | have the freedom to distribute copies of free software (and charge for
28 | them if you wish), that you receive source code or can get it if you
29 | want it, that you can change the software or use pieces of it in new
30 | free programs, and that you know you can do these things.
31 |
32 | To protect your rights, we need to prevent others from denying you
33 | these rights or asking you to surrender the rights. Therefore, you
34 | have certain responsibilities if you distribute copies of the
35 | software, or if you modify it: responsibilities to respect the freedom
36 | of others.
37 |
38 | For example, if you distribute copies of such a program, whether
39 | gratis or for a fee, you must pass on to the recipients the same
40 | freedoms that you received. You must make sure that they, too, receive
41 | or can get the source code. And you must show them these terms so they
42 | know their rights.
43 |
44 | Developers that use the GNU GPL protect your rights with two steps:
45 | (1) assert copyright on the software, and (2) offer you this License
46 | giving you legal permission to copy, distribute and/or modify it.
47 |
48 | For the developers' and authors' protection, the GPL clearly explains
49 | that there is no warranty for this free software. For both users' and
50 | authors' sake, the GPL requires that modified versions be marked as
51 | changed, so that their problems will not be attributed erroneously to
52 | authors of previous versions.
53 |
54 | Some devices are designed to deny users access to install or run
55 | modified versions of the software inside them, although the
56 | manufacturer can do so. This is fundamentally incompatible with the
57 | aim of protecting users' freedom to change the software. The
58 | systematic pattern of such abuse occurs in the area of products for
59 | individuals to use, which is precisely where it is most unacceptable.
60 | Therefore, we have designed this version of the GPL to prohibit the
61 | practice for those products. If such problems arise substantially in
62 | other domains, we stand ready to extend this provision to those
63 | domains in future versions of the GPL, as needed to protect the
64 | freedom of users.
65 |
66 | Finally, every program is threatened constantly by software patents.
67 | States should not allow patents to restrict development and use of
68 | software on general-purpose computers, but in those that do, we wish
69 | to avoid the special danger that patents applied to a free program
70 | could make it effectively proprietary. To prevent this, the GPL
71 | assures that patents cannot be used to render the program non-free.
72 |
73 | The precise terms and conditions for copying, distribution and
74 | modification follow.
75 |
76 | ## TERMS AND CONDITIONS
77 |
78 | ### 0. Definitions.
79 |
80 | "This License" refers to version 3 of the GNU General Public License.
81 |
82 | "Copyright" also means copyright-like laws that apply to other kinds
83 | of works, such as semiconductor masks.
84 |
85 | "The Program" refers to any copyrightable work licensed under this
86 | License. Each licensee is addressed as "you". "Licensees" and
87 | "recipients" may be individuals or organizations.
88 |
89 | To "modify" a work means to copy from or adapt all or part of the work
90 | in a fashion requiring copyright permission, other than the making of
91 | an exact copy. The resulting work is called a "modified version" of
92 | the earlier work or a work "based on" the earlier work.
93 |
94 | A "covered work" means either the unmodified Program or a work based
95 | on the Program.
96 |
97 | To "propagate" a work means to do anything with it that, without
98 | permission, would make you directly or secondarily liable for
99 | infringement under applicable copyright law, except executing it on a
100 | computer or modifying a private copy. Propagation includes copying,
101 | distribution (with or without modification), making available to the
102 | public, and in some countries other activities as well.
103 |
104 | To "convey" a work means any kind of propagation that enables other
105 | parties to make or receive copies. Mere interaction with a user
106 | through a computer network, with no transfer of a copy, is not
107 | conveying.
108 |
109 | An interactive user interface displays "Appropriate Legal Notices" to
110 | the extent that it includes a convenient and prominently visible
111 | feature that (1) displays an appropriate copyright notice, and (2)
112 | tells the user that there is no warranty for the work (except to the
113 | extent that warranties are provided), that licensees may convey the
114 | work under this License, and how to view a copy of this License. If
115 | the interface presents a list of user commands or options, such as a
116 | menu, a prominent item in the list meets this criterion.
117 |
118 | ### 1. Source Code.
119 |
120 | The "source code" for a work means the preferred form of the work for
121 | making modifications to it. "Object code" means any non-source form of
122 | a work.
123 |
124 | A "Standard Interface" means an interface that either is an official
125 | standard defined by a recognized standards body, or, in the case of
126 | interfaces specified for a particular programming language, one that
127 | is widely used among developers working in that language.
128 |
129 | The "System Libraries" of an executable work include anything, other
130 | than the work as a whole, that (a) is included in the normal form of
131 | packaging a Major Component, but which is not part of that Major
132 | Component, and (b) serves only to enable use of the work with that
133 | Major Component, or to implement a Standard Interface for which an
134 | implementation is available to the public in source code form. A
135 | "Major Component", in this context, means a major essential component
136 | (kernel, window system, and so on) of the specific operating system
137 | (if any) on which the executable work runs, or a compiler used to
138 | produce the work, or an object code interpreter used to run it.
139 |
140 | The "Corresponding Source" for a work in object code form means all
141 | the source code needed to generate, install, and (for an executable
142 | work) run the object code and to modify the work, including scripts to
143 | control those activities. However, it does not include the work's
144 | System Libraries, or general-purpose tools or generally available free
145 | programs which are used unmodified in performing those activities but
146 | which are not part of the work. For example, Corresponding Source
147 | includes interface definition files associated with source files for
148 | the work, and the source code for shared libraries and dynamically
149 | linked subprograms that the work is specifically designed to require,
150 | such as by intimate data communication or control flow between those
151 | subprograms and other parts of the work.
152 |
153 | The Corresponding Source need not include anything that users can
154 | regenerate automatically from other parts of the Corresponding Source.
155 |
156 | The Corresponding Source for a work in source code form is that same
157 | work.
158 |
159 | ### 2. Basic Permissions.
160 |
161 | All rights granted under this License are granted for the term of
162 | copyright on the Program, and are irrevocable provided the stated
163 | conditions are met. This License explicitly affirms your unlimited
164 | permission to run the unmodified Program. The output from running a
165 | covered work is covered by this License only if the output, given its
166 | content, constitutes a covered work. This License acknowledges your
167 | rights of fair use or other equivalent, as provided by copyright law.
168 |
169 | You may make, run and propagate covered works that you do not convey,
170 | without conditions so long as your license otherwise remains in force.
171 | You may convey covered works to others for the sole purpose of having
172 | them make modifications exclusively for you, or provide you with
173 | facilities for running those works, provided that you comply with the
174 | terms of this License in conveying all material for which you do not
175 | control copyright. Those thus making or running the covered works for
176 | you must do so exclusively on your behalf, under your direction and
177 | control, on terms that prohibit them from making any copies of your
178 | copyrighted material outside their relationship with you.
179 |
180 | Conveying under any other circumstances is permitted solely under the
181 | conditions stated below. Sublicensing is not allowed; section 10 makes
182 | it unnecessary.
183 |
184 | ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
185 |
186 | No covered work shall be deemed part of an effective technological
187 | measure under any applicable law fulfilling obligations under article
188 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
189 | similar laws prohibiting or restricting circumvention of such
190 | measures.
191 |
192 | When you convey a covered work, you waive any legal power to forbid
193 | circumvention of technological measures to the extent such
194 | circumvention is effected by exercising rights under this License with
195 | respect to the covered work, and you disclaim any intention to limit
196 | operation or modification of the work as a means of enforcing, against
197 | the work's users, your or third parties' legal rights to forbid
198 | circumvention of technological measures.
199 |
200 | ### 4. Conveying Verbatim Copies.
201 |
202 | You may convey verbatim copies of the Program's source code as you
203 | receive it, in any medium, provided that you conspicuously and
204 | appropriately publish on each copy an appropriate copyright notice;
205 | keep intact all notices stating that this License and any
206 | non-permissive terms added in accord with section 7 apply to the code;
207 | keep intact all notices of the absence of any warranty; and give all
208 | recipients a copy of this License along with the Program.
209 |
210 | You may charge any price or no price for each copy that you convey,
211 | and you may offer support or warranty protection for a fee.
212 |
213 | ### 5. Conveying Modified Source Versions.
214 |
215 | You may convey a work based on the Program, or the modifications to
216 | produce it from the Program, in the form of source code under the
217 | terms of section 4, provided that you also meet all of these
218 | conditions:
219 |
220 | - a) The work must carry prominent notices stating that you modified
221 | it, and giving a relevant date.
222 | - b) The work must carry prominent notices stating that it is
223 | released under this License and any conditions added under
224 | section 7. This requirement modifies the requirement in section 4
225 | to "keep intact all notices".
226 | - c) You must license the entire work, as a whole, under this
227 | License to anyone who comes into possession of a copy. This
228 | License will therefore apply, along with any applicable section 7
229 | additional terms, to the whole of the work, and all its parts,
230 | regardless of how they are packaged. This License gives no
231 | permission to license the work in any other way, but it does not
232 | invalidate such permission if you have separately received it.
233 | - d) If the work has interactive user interfaces, each must display
234 | Appropriate Legal Notices; however, if the Program has interactive
235 | interfaces that do not display Appropriate Legal Notices, your
236 | work need not make them do so.
237 |
238 | A compilation of a covered work with other separate and independent
239 | works, which are not by their nature extensions of the covered work,
240 | and which are not combined with it such as to form a larger program,
241 | in or on a volume of a storage or distribution medium, is called an
242 | "aggregate" if the compilation and its resulting copyright are not
243 | used to limit the access or legal rights of the compilation's users
244 | beyond what the individual works permit. Inclusion of a covered work
245 | in an aggregate does not cause this License to apply to the other
246 | parts of the aggregate.
247 |
248 | ### 6. Conveying Non-Source Forms.
249 |
250 | You may convey a covered work in object code form under the terms of
251 | sections 4 and 5, provided that you also convey the machine-readable
252 | Corresponding Source under the terms of this License, in one of these
253 | ways:
254 |
255 | - a) Convey the object code in, or embodied in, a physical product
256 | (including a physical distribution medium), accompanied by the
257 | Corresponding Source fixed on a durable physical medium
258 | customarily used for software interchange.
259 | - b) Convey the object code in, or embodied in, a physical product
260 | (including a physical distribution medium), accompanied by a
261 | written offer, valid for at least three years and valid for as
262 | long as you offer spare parts or customer support for that product
263 | model, to give anyone who possesses the object code either (1) a
264 | copy of the Corresponding Source for all the software in the
265 | product that is covered by this License, on a durable physical
266 | medium customarily used for software interchange, for a price no
267 | more than your reasonable cost of physically performing this
268 | conveying of source, or (2) access to copy the Corresponding
269 | Source from a network server at no charge.
270 | - c) Convey individual copies of the object code with a copy of the
271 | written offer to provide the Corresponding Source. This
272 | alternative is allowed only occasionally and noncommercially, and
273 | only if you received the object code with such an offer, in accord
274 | with subsection 6b.
275 | - d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 | - e) Convey the object code using peer-to-peer transmission,
288 | provided you inform other peers where the object code and
289 | Corresponding Source of the work are being offered to the general
290 | public at no charge under subsection 6d.
291 |
292 | A separable portion of the object code, whose source code is excluded
293 | from the Corresponding Source as a System Library, need not be
294 | included in conveying the object code work.
295 |
296 | A "User Product" is either (1) a "consumer product", which means any
297 | tangible personal property which is normally used for personal,
298 | family, or household purposes, or (2) anything designed or sold for
299 | incorporation into a dwelling. In determining whether a product is a
300 | consumer product, doubtful cases shall be resolved in favor of
301 | coverage. For a particular product received by a particular user,
302 | "normally used" refers to a typical or common use of that class of
303 | product, regardless of the status of the particular user or of the way
304 | in which the particular user actually uses, or expects or is expected
305 | to use, the product. A product is a consumer product regardless of
306 | whether the product has substantial commercial, industrial or
307 | non-consumer uses, unless such uses represent the only significant
308 | mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to
312 | install and execute modified versions of a covered work in that User
313 | Product from a modified version of its Corresponding Source. The
314 | information must suffice to ensure that the continued functioning of
315 | the modified object code is in no case prevented or interfered with
316 | solely because modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or
331 | updates for a work that has been modified or installed by the
332 | recipient, or for the User Product in which it has been modified or
333 | installed. Access to a network may be denied when the modification
334 | itself materially and adversely affects the operation of the network
335 | or violates the rules and protocols for communication across the
336 | network.
337 |
338 | Corresponding Source conveyed, and Installation Information provided,
339 | in accord with this section must be in a format that is publicly
340 | documented (and with an implementation available to the public in
341 | source code form), and must require no special password or key for
342 | unpacking, reading or copying.
343 |
344 | ### 7. Additional Terms.
345 |
346 | "Additional permissions" are terms that supplement the terms of this
347 | License by making exceptions from one or more of its conditions.
348 | Additional permissions that are applicable to the entire Program shall
349 | be treated as though they were included in this License, to the extent
350 | that they are valid under applicable law. If additional permissions
351 | apply only to part of the Program, that part may be used separately
352 | under those permissions, but the entire Program remains governed by
353 | this License without regard to the additional permissions.
354 |
355 | When you convey a copy of a covered work, you may at your option
356 | remove any additional permissions from that copy, or from any part of
357 | it. (Additional permissions may be written to require their own
358 | removal in certain cases when you modify the work.) You may place
359 | additional permissions on material, added by you to a covered work,
360 | for which you have or can give appropriate copyright permission.
361 |
362 | Notwithstanding any other provision of this License, for material you
363 | add to a covered work, you may (if authorized by the copyright holders
364 | of that material) supplement the terms of this License with terms:
365 |
366 | - a) Disclaiming warranty or limiting liability differently from the
367 | terms of sections 15 and 16 of this License; or
368 | - b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 | - c) Prohibiting misrepresentation of the origin of that material,
372 | or requiring that modified versions of such material be marked in
373 | reasonable ways as different from the original version; or
374 | - d) Limiting the use for publicity purposes of names of licensors
375 | or authors of the material; or
376 | - e) Declining to grant rights under trademark law for use of some
377 | trade names, trademarks, or service marks; or
378 | - f) Requiring indemnification of licensors and authors of that
379 | material by anyone who conveys the material (or modified versions
380 | of it) with contractual assumptions of liability to the recipient,
381 | for any liability that these contractual assumptions directly
382 | impose on those licensors and authors.
383 |
384 | All other non-permissive additional terms are considered "further
385 | restrictions" within the meaning of section 10. If the Program as you
386 | received it, or any part of it, contains a notice stating that it is
387 | governed by this License along with a term that is a further
388 | restriction, you may remove that term. If a license document contains
389 | a further restriction but permits relicensing or conveying under this
390 | License, you may add to a covered work material governed by the terms
391 | of that license document, provided that the further restriction does
392 | not survive such relicensing or conveying.
393 |
394 | If you add terms to a covered work in accord with this section, you
395 | must place, in the relevant source files, a statement of the
396 | additional terms that apply to those files, or a notice indicating
397 | where to find the applicable terms.
398 |
399 | Additional terms, permissive or non-permissive, may be stated in the
400 | form of a separately written license, or stated as exceptions; the
401 | above requirements apply either way.
402 |
403 | ### 8. Termination.
404 |
405 | You may not propagate or modify a covered work except as expressly
406 | provided under this License. Any attempt otherwise to propagate or
407 | modify it is void, and will automatically terminate your rights under
408 | this License (including any patent licenses granted under the third
409 | paragraph of section 11).
410 |
411 | However, if you cease all violation of this License, then your license
412 | from a particular copyright holder is reinstated (a) provisionally,
413 | unless and until the copyright holder explicitly and finally
414 | terminates your license, and (b) permanently, if the copyright holder
415 | fails to notify you of the violation by some reasonable means prior to
416 | 60 days after the cessation.
417 |
418 | Moreover, your license from a particular copyright holder is
419 | reinstated permanently if the copyright holder notifies you of the
420 | violation by some reasonable means, this is the first time you have
421 | received notice of violation of this License (for any work) from that
422 | copyright holder, and you cure the violation prior to 30 days after
423 | your receipt of the notice.
424 |
425 | Termination of your rights under this section does not terminate the
426 | licenses of parties who have received copies or rights from you under
427 | this License. If your rights have been terminated and not permanently
428 | reinstated, you do not qualify to receive new licenses for the same
429 | material under section 10.
430 |
431 | ### 9. Acceptance Not Required for Having Copies.
432 |
433 | You are not required to accept this License in order to receive or run
434 | a copy of the Program. Ancillary propagation of a covered work
435 | occurring solely as a consequence of using peer-to-peer transmission
436 | to receive a copy likewise does not require acceptance. However,
437 | nothing other than this License grants you permission to propagate or
438 | modify any covered work. These actions infringe copyright if you do
439 | not accept this License. Therefore, by modifying or propagating a
440 | covered work, you indicate your acceptance of this License to do so.
441 |
442 | ### 10. Automatic Licensing of Downstream Recipients.
443 |
444 | Each time you convey a covered work, the recipient automatically
445 | receives a license from the original licensors, to run, modify and
446 | propagate that work, subject to this License. You are not responsible
447 | for enforcing compliance by third parties with this License.
448 |
449 | An "entity transaction" is a transaction transferring control of an
450 | organization, or substantially all assets of one, or subdividing an
451 | organization, or merging organizations. If propagation of a covered
452 | work results from an entity transaction, each party to that
453 | transaction who receives a copy of the work also receives whatever
454 | licenses to the work the party's predecessor in interest had or could
455 | give under the previous paragraph, plus a right to possession of the
456 | Corresponding Source of the work from the predecessor in interest, if
457 | the predecessor has it or can get it with reasonable efforts.
458 |
459 | You may not impose any further restrictions on the exercise of the
460 | rights granted or affirmed under this License. For example, you may
461 | not impose a license fee, royalty, or other charge for exercise of
462 | rights granted under this License, and you may not initiate litigation
463 | (including a cross-claim or counterclaim in a lawsuit) alleging that
464 | any patent claim is infringed by making, using, selling, offering for
465 | sale, or importing the Program or any portion of it.
466 |
467 | ### 11. Patents.
468 |
469 | A "contributor" is a copyright holder who authorizes use under this
470 | License of the Program or a work on which the Program is based. The
471 | work thus licensed is called the contributor's "contributor version".
472 |
473 | A contributor's "essential patent claims" are all patent claims owned
474 | or controlled by the contributor, whether already acquired or
475 | hereafter acquired, that would be infringed by some manner, permitted
476 | by this License, of making, using, or selling its contributor version,
477 | but do not include claims that would be infringed only as a
478 | consequence of further modification of the contributor version. For
479 | purposes of this definition, "control" includes the right to grant
480 | patent sublicenses in a manner consistent with the requirements of
481 | this License.
482 |
483 | Each contributor grants you a non-exclusive, worldwide, royalty-free
484 | patent license under the contributor's essential patent claims, to
485 | make, use, sell, offer for sale, import and otherwise run, modify and
486 | propagate the contents of its contributor version.
487 |
488 | In the following three paragraphs, a "patent license" is any express
489 | agreement or commitment, however denominated, not to enforce a patent
490 | (such as an express permission to practice a patent or covenant not to
491 | sue for patent infringement). To "grant" such a patent license to a
492 | party means to make such an agreement or commitment not to enforce a
493 | patent against the party.
494 |
495 | If you convey a covered work, knowingly relying on a patent license,
496 | and the Corresponding Source of the work is not available for anyone
497 | to copy, free of charge and under the terms of this License, through a
498 | publicly available network server or other readily accessible means,
499 | then you must either (1) cause the Corresponding Source to be so
500 | available, or (2) arrange to deprive yourself of the benefit of the
501 | patent license for this particular work, or (3) arrange, in a manner
502 | consistent with the requirements of this License, to extend the patent
503 | license to downstream recipients. "Knowingly relying" means you have
504 | actual knowledge that, but for the patent license, your conveying the
505 | covered work in a country, or your recipient's use of the covered work
506 | in a country, would infringe one or more identifiable patents in that
507 | country that you have reason to believe are valid.
508 |
509 | If, pursuant to or in connection with a single transaction or
510 | arrangement, you convey, or propagate by procuring conveyance of, a
511 | covered work, and grant a patent license to some of the parties
512 | receiving the covered work authorizing them to use, propagate, modify
513 | or convey a specific copy of the covered work, then the patent license
514 | you grant is automatically extended to all recipients of the covered
515 | work and works based on it.
516 |
517 | A patent license is "discriminatory" if it does not include within the
518 | scope of its coverage, prohibits the exercise of, or is conditioned on
519 | the non-exercise of one or more of the rights that are specifically
520 | granted under this License. You may not convey a covered work if you
521 | are a party to an arrangement with a third party that is in the
522 | business of distributing software, under which you make payment to the
523 | third party based on the extent of your activity of conveying the
524 | work, and under which the third party grants, to any of the parties
525 | who would receive the covered work from you, a discriminatory patent
526 | license (a) in connection with copies of the covered work conveyed by
527 | you (or copies made from those copies), or (b) primarily for and in
528 | connection with specific products or compilations that contain the
529 | covered work, unless you entered into that arrangement, or that patent
530 | license was granted, prior to 28 March 2007.
531 |
532 | Nothing in this License shall be construed as excluding or limiting
533 | any implied license or other defenses to infringement that may
534 | otherwise be available to you under applicable patent law.
535 |
536 | ### 12. No Surrender of Others' Freedom.
537 |
538 | If conditions are imposed on you (whether by court order, agreement or
539 | otherwise) that contradict the conditions of this License, they do not
540 | excuse you from the conditions of this License. If you cannot convey a
541 | covered work so as to satisfy simultaneously your obligations under
542 | this License and any other pertinent obligations, then as a
543 | consequence you may not convey it at all. For example, if you agree to
544 | terms that obligate you to collect a royalty for further conveying
545 | from those to whom you convey the Program, the only way you could
546 | satisfy both those terms and this License would be to refrain entirely
547 | from conveying the Program.
548 |
549 | ### 13. Use with the GNU Affero General Public License.
550 |
551 | Notwithstanding any other provision of this License, you have
552 | permission to link or combine any covered work with a work licensed
553 | under version 3 of the GNU Affero General Public License into a single
554 | combined work, and to convey the resulting work. The terms of this
555 | License will continue to apply to the part which is the covered work,
556 | but the special requirements of the GNU Affero General Public License,
557 | section 13, concerning interaction through a network will apply to the
558 | combination as such.
559 |
560 | ### 14. Revised Versions of this License.
561 |
562 | The Free Software Foundation may publish revised and/or new versions
563 | of the GNU General Public License from time to time. Such new versions
564 | will be similar in spirit to the present version, but may differ in
565 | detail to address new problems or concerns.
566 |
567 | Each version is given a distinguishing version number. If the Program
568 | specifies that a certain numbered version of the GNU General Public
569 | License "or any later version" applies to it, you have the option of
570 | following the terms and conditions either of that numbered version or
571 | of any later version published by the Free Software Foundation. If the
572 | Program does not specify a version number of the GNU General Public
573 | License, you may choose any version ever published by the Free
574 | Software Foundation.
575 |
576 | If the Program specifies that a proxy can decide which future versions
577 | of the GNU General Public License can be used, that proxy's public
578 | statement of acceptance of a version permanently authorizes you to
579 | choose that version for the Program.
580 |
581 | Later license versions may give you additional or different
582 | permissions. However, no additional obligations are imposed on any
583 | author or copyright holder as a result of your choosing to follow a
584 | later version.
585 |
586 | ### 15. Disclaimer of Warranty.
587 |
588 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
589 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
590 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
591 | WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
592 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
593 | A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
594 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
595 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
596 | CORRECTION.
597 |
598 | ### 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
602 | CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
603 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
604 | ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
605 | NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
606 | LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
607 | TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
608 | PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
609 |
610 | ### 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | ## How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these
626 | terms.
627 |
628 | To do so, attach the following notices to the program. It is safest to
629 | attach them to the start of each source file to most effectively state
630 | the exclusion of warranty; and each file should have at least the
631 | "copyright" line and a pointer to where the full notice is found.
632 |
633 |
634 | Copyright (C)
635 |
636 | This program is free software: you can redistribute it and/or modify
637 | it under the terms of the GNU General Public License as published by
638 | the Free Software Foundation, either version 3 of the License, or
639 | (at your option) any later version.
640 |
641 | This program is distributed in the hope that it will be useful,
642 | but WITHOUT ANY WARRANTY; without even the implied warranty of
643 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
644 | GNU General Public License for more details.
645 |
646 | You should have received a copy of the GNU General Public License
647 | along with this program. If not, see .
648 |
649 | Also add information on how to contact you by electronic and paper
650 | mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands \`show w' and \`show c' should show the
661 | appropriate parts of the General Public License. Of course, your
662 | program's commands might be different; for a GUI interface, you would
663 | use an "about box".
664 |
665 | You should also get your employer (if you work as a programmer) or
666 | school, if any, to sign a "copyright disclaimer" for the program, if
667 | necessary. For more information on this, and how to apply and follow
668 | the GNU GPL, see .
669 |
670 | The GNU General Public License does not permit incorporating your
671 | program into proprietary programs. If your program is a subroutine
672 | library, you may consider it more useful to permit linking proprietary
673 | applications with the library. If this is what you want to do, use the
674 | GNU Lesser General Public License instead of this License. But first,
675 | please read .
676 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [packages]
7 | zsh-jupyter-kernel = { editable = true, path = "." }
8 | jupyterlab = "*"
9 | jsonschema = "*" # testing message specs (msgspec_v5.py)
10 | pytest = "*"
11 |
12 | [requires]
13 | python_version = "3.10"
14 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "d7a50ee2d4a48ac214c9e8fe821b6f29297c7884a7e0fff355323d142666d266"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.10"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "anyio": {
20 | "hashes": [
21 | "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48",
22 | "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"
23 | ],
24 | "markers": "python_version >= '3.9'",
25 | "version": "==4.7.0"
26 | },
27 | "argon2-cffi": {
28 | "hashes": [
29 | "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08",
30 | "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"
31 | ],
32 | "markers": "python_version >= '3.7'",
33 | "version": "==23.1.0"
34 | },
35 | "argon2-cffi-bindings": {
36 | "hashes": [
37 | "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670",
38 | "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f",
39 | "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583",
40 | "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194",
41 | "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c",
42 | "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a",
43 | "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082",
44 | "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5",
45 | "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f",
46 | "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7",
47 | "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d",
48 | "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f",
49 | "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae",
50 | "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3",
51 | "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86",
52 | "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367",
53 | "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d",
54 | "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93",
55 | "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb",
56 | "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e",
57 | "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"
58 | ],
59 | "markers": "python_version >= '3.6'",
60 | "version": "==21.2.0"
61 | },
62 | "arrow": {
63 | "hashes": [
64 | "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80",
65 | "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"
66 | ],
67 | "markers": "python_version >= '3.8'",
68 | "version": "==1.3.0"
69 | },
70 | "asttokens": {
71 | "hashes": [
72 | "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7",
73 | "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"
74 | ],
75 | "markers": "python_version >= '3.8'",
76 | "version": "==3.0.0"
77 | },
78 | "async-lru": {
79 | "hashes": [
80 | "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627",
81 | "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"
82 | ],
83 | "markers": "python_version >= '3.8'",
84 | "version": "==2.0.4"
85 | },
86 | "attrs": {
87 | "hashes": [
88 | "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff",
89 | "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"
90 | ],
91 | "markers": "python_version >= '3.8'",
92 | "version": "==24.3.0"
93 | },
94 | "babel": {
95 | "hashes": [
96 | "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b",
97 | "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"
98 | ],
99 | "markers": "python_version >= '3.8'",
100 | "version": "==2.16.0"
101 | },
102 | "beautifulsoup4": {
103 | "hashes": [
104 | "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051",
105 | "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"
106 | ],
107 | "markers": "python_full_version >= '3.6.0'",
108 | "version": "==4.12.3"
109 | },
110 | "bleach": {
111 | "hashes": [
112 | "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e",
113 | "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"
114 | ],
115 | "markers": "python_version >= '3.9'",
116 | "version": "==6.2.0"
117 | },
118 | "certifi": {
119 | "hashes": [
120 | "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56",
121 | "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"
122 | ],
123 | "markers": "python_version >= '3.6'",
124 | "version": "==2024.12.14"
125 | },
126 | "cffi": {
127 | "hashes": [
128 | "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
129 | "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
130 | "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
131 | "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
132 | "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
133 | "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
134 | "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
135 | "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
136 | "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
137 | "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
138 | "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
139 | "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
140 | "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
141 | "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
142 | "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
143 | "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
144 | "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
145 | "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
146 | "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
147 | "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
148 | "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
149 | "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
150 | "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
151 | "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
152 | "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
153 | "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
154 | "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
155 | "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
156 | "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
157 | "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
158 | "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
159 | "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
160 | "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
161 | "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
162 | "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
163 | "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
164 | "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
165 | "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
166 | "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
167 | "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
168 | "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
169 | "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
170 | "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
171 | "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
172 | "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
173 | "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
174 | "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
175 | "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
176 | "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
177 | "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
178 | "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
179 | "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
180 | "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
181 | "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
182 | "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
183 | "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
184 | "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
185 | "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
186 | "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
187 | "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
188 | "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
189 | "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
190 | "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
191 | "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
192 | "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
193 | "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
194 | "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
195 | ],
196 | "markers": "python_version >= '3.8'",
197 | "version": "==1.17.1"
198 | },
199 | "charset-normalizer": {
200 | "hashes": [
201 | "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537",
202 | "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa",
203 | "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a",
204 | "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294",
205 | "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b",
206 | "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd",
207 | "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601",
208 | "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd",
209 | "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4",
210 | "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d",
211 | "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2",
212 | "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313",
213 | "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd",
214 | "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa",
215 | "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8",
216 | "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1",
217 | "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2",
218 | "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496",
219 | "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d",
220 | "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b",
221 | "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e",
222 | "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a",
223 | "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4",
224 | "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca",
225 | "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78",
226 | "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408",
227 | "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5",
228 | "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3",
229 | "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f",
230 | "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a",
231 | "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765",
232 | "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6",
233 | "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146",
234 | "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6",
235 | "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9",
236 | "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd",
237 | "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c",
238 | "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f",
239 | "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545",
240 | "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176",
241 | "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770",
242 | "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824",
243 | "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f",
244 | "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf",
245 | "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487",
246 | "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d",
247 | "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd",
248 | "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b",
249 | "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534",
250 | "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f",
251 | "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b",
252 | "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9",
253 | "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd",
254 | "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125",
255 | "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9",
256 | "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de",
257 | "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11",
258 | "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d",
259 | "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35",
260 | "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f",
261 | "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda",
262 | "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7",
263 | "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a",
264 | "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971",
265 | "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8",
266 | "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41",
267 | "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d",
268 | "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f",
269 | "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757",
270 | "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a",
271 | "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886",
272 | "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77",
273 | "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76",
274 | "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247",
275 | "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85",
276 | "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb",
277 | "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7",
278 | "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e",
279 | "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6",
280 | "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037",
281 | "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1",
282 | "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e",
283 | "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807",
284 | "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407",
285 | "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c",
286 | "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12",
287 | "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3",
288 | "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089",
289 | "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd",
290 | "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e",
291 | "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00",
292 | "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"
293 | ],
294 | "markers": "python_version >= '3.7'",
295 | "version": "==3.4.1"
296 | },
297 | "comm": {
298 | "hashes": [
299 | "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e",
300 | "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"
301 | ],
302 | "markers": "python_version >= '3.8'",
303 | "version": "==0.2.2"
304 | },
305 | "debugpy": {
306 | "hashes": [
307 | "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920",
308 | "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5",
309 | "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0",
310 | "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851",
311 | "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b",
312 | "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd",
313 | "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280",
314 | "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9",
315 | "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1",
316 | "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0",
317 | "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7",
318 | "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f",
319 | "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458",
320 | "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57",
321 | "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e",
322 | "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308",
323 | "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296",
324 | "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3",
325 | "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1",
326 | "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1",
327 | "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e",
328 | "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db",
329 | "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28",
330 | "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737",
331 | "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768",
332 | "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"
333 | ],
334 | "markers": "python_version >= '3.8'",
335 | "version": "==1.8.11"
336 | },
337 | "decorator": {
338 | "hashes": [
339 | "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330",
340 | "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"
341 | ],
342 | "markers": "python_version >= '3.5'",
343 | "version": "==5.1.1"
344 | },
345 | "defusedxml": {
346 | "hashes": [
347 | "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
348 | "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
349 | ],
350 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
351 | "version": "==0.7.1"
352 | },
353 | "exceptiongroup": {
354 | "hashes": [
355 | "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b",
356 | "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"
357 | ],
358 | "markers": "python_version >= '3.7'",
359 | "version": "==1.2.2"
360 | },
361 | "executing": {
362 | "hashes": [
363 | "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf",
364 | "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"
365 | ],
366 | "markers": "python_version >= '3.8'",
367 | "version": "==2.1.0"
368 | },
369 | "fastjsonschema": {
370 | "hashes": [
371 | "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4",
372 | "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"
373 | ],
374 | "version": "==2.21.1"
375 | },
376 | "fqdn": {
377 | "hashes": [
378 | "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f",
379 | "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"
380 | ],
381 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
382 | "version": "==1.5.1"
383 | },
384 | "h11": {
385 | "hashes": [
386 | "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
387 | "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
388 | ],
389 | "markers": "python_version >= '3.7'",
390 | "version": "==0.14.0"
391 | },
392 | "httpcore": {
393 | "hashes": [
394 | "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c",
395 | "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"
396 | ],
397 | "markers": "python_version >= '3.8'",
398 | "version": "==1.0.7"
399 | },
400 | "httpx": {
401 | "hashes": [
402 | "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc",
403 | "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"
404 | ],
405 | "markers": "python_version >= '3.8'",
406 | "version": "==0.28.1"
407 | },
408 | "idna": {
409 | "hashes": [
410 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
411 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
412 | ],
413 | "markers": "python_version >= '3.6'",
414 | "version": "==3.10"
415 | },
416 | "iniconfig": {
417 | "hashes": [
418 | "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
419 | "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
420 | ],
421 | "markers": "python_version >= '3.7'",
422 | "version": "==2.0.0"
423 | },
424 | "ipykernel": {
425 | "hashes": [
426 | "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5",
427 | "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215"
428 | ],
429 | "markers": "python_version >= '3.8'",
430 | "version": "==6.29.5"
431 | },
432 | "ipython": {
433 | "hashes": [
434 | "sha256:46ec58f8d3d076a61d128fe517a51eb730e3aaf0c184ea8c17d16e366660c6a6",
435 | "sha256:b6a2274606bec6166405ff05e54932ed6e5cfecaca1fc05f2cacde7bb074d70b"
436 | ],
437 | "markers": "python_version >= '3.10'",
438 | "version": "==8.31.0"
439 | },
440 | "isoduration": {
441 | "hashes": [
442 | "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9",
443 | "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"
444 | ],
445 | "markers": "python_version >= '3.7'",
446 | "version": "==20.11.0"
447 | },
448 | "jedi": {
449 | "hashes": [
450 | "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0",
451 | "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"
452 | ],
453 | "markers": "python_version >= '3.6'",
454 | "version": "==0.19.2"
455 | },
456 | "jinja2": {
457 | "hashes": [
458 | "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb",
459 | "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"
460 | ],
461 | "markers": "python_version >= '3.7'",
462 | "version": "==3.1.5"
463 | },
464 | "json5": {
465 | "hashes": [
466 | "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa",
467 | "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"
468 | ],
469 | "markers": "python_full_version >= '3.8.0'",
470 | "version": "==0.10.0"
471 | },
472 | "jsonpointer": {
473 | "hashes": [
474 | "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942",
475 | "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"
476 | ],
477 | "markers": "python_version >= '3.7'",
478 | "version": "==3.0.0"
479 | },
480 | "jsonschema": {
481 | "extras": [
482 | "format-nongpl"
483 | ],
484 | "hashes": [
485 | "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4",
486 | "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"
487 | ],
488 | "index": "pypi",
489 | "markers": "python_version >= '3.8'",
490 | "version": "==4.23.0"
491 | },
492 | "jsonschema-specifications": {
493 | "hashes": [
494 | "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272",
495 | "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"
496 | ],
497 | "markers": "python_version >= '3.9'",
498 | "version": "==2024.10.1"
499 | },
500 | "jupyter-client": {
501 | "hashes": [
502 | "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419",
503 | "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"
504 | ],
505 | "markers": "python_version >= '3.8'",
506 | "version": "==8.6.3"
507 | },
508 | "jupyter-core": {
509 | "hashes": [
510 | "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409",
511 | "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"
512 | ],
513 | "markers": "python_version >= '3.8'",
514 | "version": "==5.7.2"
515 | },
516 | "jupyter-events": {
517 | "hashes": [
518 | "sha256:36399b41ce1ca45fe8b8271067d6a140ffa54cec4028e95491c93b78a855cacf",
519 | "sha256:c0bc56a37aac29c1fbc3bcfbddb8c8c49533f9cf11f1c4e6adadba936574ab90"
520 | ],
521 | "markers": "python_version >= '3.9'",
522 | "version": "==0.11.0"
523 | },
524 | "jupyter-lsp": {
525 | "hashes": [
526 | "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da",
527 | "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001"
528 | ],
529 | "markers": "python_version >= '3.8'",
530 | "version": "==2.2.5"
531 | },
532 | "jupyter-server": {
533 | "hashes": [
534 | "sha256:872d989becf83517012ee669f09604aa4a28097c0bd90b2f424310156c2cdae3",
535 | "sha256:9d446b8697b4f7337a1b7cdcac40778babdd93ba614b6d68ab1c0c918f1c4084"
536 | ],
537 | "markers": "python_version >= '3.9'",
538 | "version": "==2.15.0"
539 | },
540 | "jupyter-server-terminals": {
541 | "hashes": [
542 | "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa",
543 | "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"
544 | ],
545 | "markers": "python_version >= '3.8'",
546 | "version": "==0.5.3"
547 | },
548 | "jupyterlab": {
549 | "hashes": [
550 | "sha256:b754c2601c5be6adf87cb5a1d8495d653ffb945f021939f77776acaa94dae952",
551 | "sha256:f0bb9b09a04766e3423cccc2fc23169aa2ffedcdf8713e9e0fb33cac0b6859d0"
552 | ],
553 | "index": "pypi",
554 | "markers": "python_version >= '3.8'",
555 | "version": "==4.3.4"
556 | },
557 | "jupyterlab-pygments": {
558 | "hashes": [
559 | "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d",
560 | "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"
561 | ],
562 | "markers": "python_version >= '3.8'",
563 | "version": "==0.3.0"
564 | },
565 | "jupyterlab-server": {
566 | "hashes": [
567 | "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4",
568 | "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4"
569 | ],
570 | "markers": "python_version >= '3.8'",
571 | "version": "==2.27.3"
572 | },
573 | "markupsafe": {
574 | "hashes": [
575 | "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4",
576 | "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30",
577 | "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0",
578 | "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9",
579 | "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396",
580 | "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13",
581 | "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028",
582 | "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca",
583 | "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557",
584 | "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832",
585 | "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0",
586 | "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b",
587 | "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579",
588 | "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a",
589 | "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c",
590 | "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff",
591 | "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c",
592 | "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22",
593 | "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094",
594 | "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb",
595 | "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e",
596 | "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5",
597 | "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a",
598 | "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d",
599 | "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a",
600 | "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b",
601 | "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8",
602 | "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225",
603 | "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c",
604 | "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144",
605 | "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f",
606 | "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87",
607 | "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d",
608 | "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93",
609 | "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf",
610 | "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158",
611 | "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84",
612 | "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb",
613 | "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48",
614 | "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171",
615 | "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c",
616 | "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6",
617 | "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd",
618 | "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d",
619 | "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1",
620 | "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d",
621 | "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca",
622 | "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a",
623 | "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29",
624 | "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe",
625 | "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798",
626 | "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c",
627 | "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8",
628 | "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f",
629 | "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f",
630 | "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a",
631 | "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178",
632 | "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0",
633 | "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79",
634 | "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430",
635 | "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"
636 | ],
637 | "markers": "python_version >= '3.9'",
638 | "version": "==3.0.2"
639 | },
640 | "matplotlib-inline": {
641 | "hashes": [
642 | "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90",
643 | "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"
644 | ],
645 | "markers": "python_version >= '3.8'",
646 | "version": "==0.1.7"
647 | },
648 | "mistune": {
649 | "hashes": [
650 | "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1",
651 | "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667"
652 | ],
653 | "markers": "python_version >= '3.8'",
654 | "version": "==3.1.0"
655 | },
656 | "nbclient": {
657 | "hashes": [
658 | "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d",
659 | "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193"
660 | ],
661 | "markers": "python_full_version >= '3.9.0'",
662 | "version": "==0.10.2"
663 | },
664 | "nbconvert": {
665 | "hashes": [
666 | "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3",
667 | "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"
668 | ],
669 | "markers": "python_version >= '3.8'",
670 | "version": "==7.16.4"
671 | },
672 | "nbformat": {
673 | "hashes": [
674 | "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a",
675 | "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"
676 | ],
677 | "markers": "python_version >= '3.8'",
678 | "version": "==5.10.4"
679 | },
680 | "nest-asyncio": {
681 | "hashes": [
682 | "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe",
683 | "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"
684 | ],
685 | "markers": "python_version >= '3.5'",
686 | "version": "==1.6.0"
687 | },
688 | "notebook-shim": {
689 | "hashes": [
690 | "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef",
691 | "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"
692 | ],
693 | "markers": "python_version >= '3.7'",
694 | "version": "==0.2.4"
695 | },
696 | "overrides": {
697 | "hashes": [
698 | "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a",
699 | "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"
700 | ],
701 | "markers": "python_version >= '3.6'",
702 | "version": "==7.7.0"
703 | },
704 | "packaging": {
705 | "hashes": [
706 | "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759",
707 | "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"
708 | ],
709 | "markers": "python_version >= '3.8'",
710 | "version": "==24.2"
711 | },
712 | "pandocfilters": {
713 | "hashes": [
714 | "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e",
715 | "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"
716 | ],
717 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
718 | "version": "==1.5.1"
719 | },
720 | "parso": {
721 | "hashes": [
722 | "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18",
723 | "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"
724 | ],
725 | "markers": "python_version >= '3.6'",
726 | "version": "==0.8.4"
727 | },
728 | "pexpect": {
729 | "hashes": [
730 | "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523",
731 | "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"
732 | ],
733 | "version": "==4.9.0"
734 | },
735 | "platformdirs": {
736 | "hashes": [
737 | "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907",
738 | "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"
739 | ],
740 | "markers": "python_version >= '3.8'",
741 | "version": "==4.3.6"
742 | },
743 | "pluggy": {
744 | "hashes": [
745 | "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1",
746 | "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"
747 | ],
748 | "markers": "python_version >= '3.8'",
749 | "version": "==1.5.0"
750 | },
751 | "prometheus-client": {
752 | "hashes": [
753 | "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb",
754 | "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"
755 | ],
756 | "markers": "python_version >= '3.8'",
757 | "version": "==0.21.1"
758 | },
759 | "prompt-toolkit": {
760 | "hashes": [
761 | "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90",
762 | "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"
763 | ],
764 | "markers": "python_full_version >= '3.7.0'",
765 | "version": "==3.0.48"
766 | },
767 | "psutil": {
768 | "hashes": [
769 | "sha256:018aeae2af92d943fdf1da6b58665124897cfc94faa2ca92098838f83e1b1bca",
770 | "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377",
771 | "sha256:1924e659d6c19c647e763e78670a05dbb7feaf44a0e9c94bf9e14dfc6ba50468",
772 | "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3",
773 | "sha256:384636b1a64b47814437d1173be1427a7c83681b17a450bfc309a1953e329603",
774 | "sha256:6d4281f5bbca041e2292be3380ec56a9413b790579b8e593b1784499d0005dac",
775 | "sha256:8be07491f6ebe1a693f17d4f11e69d0dc1811fa082736500f649f79df7735303",
776 | "sha256:8df0178ba8a9e5bc84fed9cfa61d54601b371fbec5c8eebad27575f1e105c0d4",
777 | "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160",
778 | "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8",
779 | "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003",
780 | "sha256:c777eb75bb33c47377c9af68f30e9f11bc78e0f07fbf907be4a5d70b2fe5f030",
781 | "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777",
782 | "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5",
783 | "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53",
784 | "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649",
785 | "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8"
786 | ],
787 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
788 | "version": "==6.1.1"
789 | },
790 | "ptyprocess": {
791 | "hashes": [
792 | "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35",
793 | "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"
794 | ],
795 | "version": "==0.7.0"
796 | },
797 | "pure-eval": {
798 | "hashes": [
799 | "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0",
800 | "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"
801 | ],
802 | "version": "==0.2.3"
803 | },
804 | "pycparser": {
805 | "hashes": [
806 | "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
807 | "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
808 | ],
809 | "markers": "python_version >= '3.8'",
810 | "version": "==2.22"
811 | },
812 | "pygments": {
813 | "hashes": [
814 | "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
815 | "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"
816 | ],
817 | "markers": "python_version >= '3.8'",
818 | "version": "==2.18.0"
819 | },
820 | "pytest": {
821 | "hashes": [
822 | "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6",
823 | "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"
824 | ],
825 | "index": "pypi",
826 | "markers": "python_version >= '3.8'",
827 | "version": "==8.3.4"
828 | },
829 | "python-dateutil": {
830 | "hashes": [
831 | "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
832 | "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
833 | ],
834 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
835 | "version": "==2.9.0.post0"
836 | },
837 | "python-json-logger": {
838 | "hashes": [
839 | "sha256:8eb0554ea17cb75b05d2848bc14fb02fbdbd9d6972120781b974380bfa162008",
840 | "sha256:cdc17047eb5374bd311e748b42f99d71223f3b0e186f4206cc5d52aefe85b090"
841 | ],
842 | "markers": "python_version >= '3.8'",
843 | "version": "==3.2.1"
844 | },
845 | "pyyaml": {
846 | "hashes": [
847 | "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff",
848 | "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48",
849 | "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086",
850 | "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e",
851 | "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133",
852 | "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5",
853 | "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484",
854 | "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee",
855 | "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5",
856 | "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68",
857 | "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a",
858 | "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf",
859 | "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99",
860 | "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8",
861 | "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85",
862 | "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19",
863 | "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc",
864 | "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a",
865 | "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1",
866 | "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317",
867 | "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c",
868 | "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631",
869 | "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d",
870 | "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652",
871 | "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5",
872 | "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e",
873 | "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b",
874 | "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8",
875 | "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476",
876 | "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706",
877 | "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563",
878 | "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237",
879 | "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b",
880 | "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083",
881 | "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180",
882 | "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425",
883 | "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e",
884 | "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f",
885 | "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725",
886 | "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183",
887 | "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab",
888 | "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774",
889 | "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725",
890 | "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e",
891 | "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5",
892 | "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d",
893 | "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290",
894 | "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44",
895 | "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed",
896 | "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4",
897 | "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba",
898 | "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12",
899 | "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"
900 | ],
901 | "markers": "python_version >= '3.8'",
902 | "version": "==6.0.2"
903 | },
904 | "pyzmq": {
905 | "hashes": [
906 | "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6",
907 | "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a",
908 | "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9",
909 | "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f",
910 | "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37",
911 | "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc",
912 | "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed",
913 | "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097",
914 | "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d",
915 | "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52",
916 | "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6",
917 | "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6",
918 | "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2",
919 | "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282",
920 | "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3",
921 | "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732",
922 | "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5",
923 | "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18",
924 | "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306",
925 | "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f",
926 | "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3",
927 | "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b",
928 | "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277",
929 | "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a",
930 | "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797",
931 | "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca",
932 | "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c",
933 | "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f",
934 | "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5",
935 | "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a",
936 | "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44",
937 | "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20",
938 | "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4",
939 | "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8",
940 | "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780",
941 | "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386",
942 | "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5",
943 | "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2",
944 | "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0",
945 | "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971",
946 | "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b",
947 | "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50",
948 | "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c",
949 | "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f",
950 | "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231",
951 | "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c",
952 | "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08",
953 | "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5",
954 | "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6",
955 | "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073",
956 | "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e",
957 | "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4",
958 | "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317",
959 | "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3",
960 | "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072",
961 | "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad",
962 | "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a",
963 | "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb",
964 | "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd",
965 | "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f",
966 | "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef",
967 | "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5",
968 | "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187",
969 | "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711",
970 | "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988",
971 | "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640",
972 | "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c",
973 | "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764",
974 | "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1",
975 | "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1",
976 | "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289",
977 | "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb",
978 | "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a",
979 | "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218",
980 | "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c",
981 | "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf",
982 | "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7",
983 | "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8",
984 | "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726",
985 | "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9",
986 | "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93",
987 | "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88",
988 | "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115",
989 | "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6",
990 | "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672",
991 | "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2",
992 | "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea",
993 | "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc",
994 | "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b",
995 | "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa",
996 | "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003",
997 | "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797",
998 | "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940",
999 | "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db",
1000 | "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc",
1001 | "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27",
1002 | "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3",
1003 | "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e",
1004 | "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98",
1005 | "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b",
1006 | "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629",
1007 | "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9",
1008 | "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6",
1009 | "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec",
1010 | "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951",
1011 | "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae",
1012 | "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4",
1013 | "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6",
1014 | "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"
1015 | ],
1016 | "markers": "python_version >= '3.7'",
1017 | "version": "==26.2.0"
1018 | },
1019 | "referencing": {
1020 | "hashes": [
1021 | "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c",
1022 | "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"
1023 | ],
1024 | "markers": "python_version >= '3.8'",
1025 | "version": "==0.35.1"
1026 | },
1027 | "requests": {
1028 | "hashes": [
1029 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
1030 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
1031 | ],
1032 | "markers": "python_version >= '3.8'",
1033 | "version": "==2.32.3"
1034 | },
1035 | "rfc3339-validator": {
1036 | "hashes": [
1037 | "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b",
1038 | "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"
1039 | ],
1040 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
1041 | "version": "==0.1.4"
1042 | },
1043 | "rfc3986-validator": {
1044 | "hashes": [
1045 | "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9",
1046 | "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"
1047 | ],
1048 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
1049 | "version": "==0.1.1"
1050 | },
1051 | "rpds-py": {
1052 | "hashes": [
1053 | "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518",
1054 | "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059",
1055 | "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61",
1056 | "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5",
1057 | "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9",
1058 | "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543",
1059 | "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2",
1060 | "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a",
1061 | "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d",
1062 | "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56",
1063 | "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d",
1064 | "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd",
1065 | "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b",
1066 | "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4",
1067 | "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99",
1068 | "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d",
1069 | "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd",
1070 | "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe",
1071 | "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1",
1072 | "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e",
1073 | "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f",
1074 | "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3",
1075 | "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca",
1076 | "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d",
1077 | "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e",
1078 | "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc",
1079 | "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea",
1080 | "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38",
1081 | "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b",
1082 | "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c",
1083 | "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff",
1084 | "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723",
1085 | "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e",
1086 | "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493",
1087 | "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6",
1088 | "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83",
1089 | "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091",
1090 | "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1",
1091 | "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627",
1092 | "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1",
1093 | "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728",
1094 | "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16",
1095 | "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c",
1096 | "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45",
1097 | "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7",
1098 | "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a",
1099 | "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730",
1100 | "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967",
1101 | "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25",
1102 | "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24",
1103 | "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055",
1104 | "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d",
1105 | "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0",
1106 | "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e",
1107 | "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7",
1108 | "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c",
1109 | "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f",
1110 | "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd",
1111 | "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652",
1112 | "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8",
1113 | "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11",
1114 | "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333",
1115 | "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96",
1116 | "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64",
1117 | "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b",
1118 | "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e",
1119 | "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c",
1120 | "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9",
1121 | "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec",
1122 | "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb",
1123 | "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37",
1124 | "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad",
1125 | "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9",
1126 | "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c",
1127 | "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf",
1128 | "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4",
1129 | "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f",
1130 | "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d",
1131 | "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09",
1132 | "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d",
1133 | "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566",
1134 | "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74",
1135 | "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338",
1136 | "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15",
1137 | "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c",
1138 | "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648",
1139 | "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84",
1140 | "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3",
1141 | "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123",
1142 | "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520",
1143 | "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831",
1144 | "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e",
1145 | "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf",
1146 | "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b",
1147 | "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2",
1148 | "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3",
1149 | "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130",
1150 | "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b",
1151 | "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de",
1152 | "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5",
1153 | "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d",
1154 | "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00",
1155 | "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"
1156 | ],
1157 | "markers": "python_version >= '3.9'",
1158 | "version": "==0.22.3"
1159 | },
1160 | "send2trash": {
1161 | "hashes": [
1162 | "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9",
1163 | "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"
1164 | ],
1165 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
1166 | "version": "==1.8.3"
1167 | },
1168 | "setuptools": {
1169 | "hashes": [
1170 | "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6",
1171 | "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"
1172 | ],
1173 | "markers": "python_version >= '3.9'",
1174 | "version": "==75.6.0"
1175 | },
1176 | "six": {
1177 | "hashes": [
1178 | "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274",
1179 | "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
1180 | ],
1181 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
1182 | "version": "==1.17.0"
1183 | },
1184 | "sniffio": {
1185 | "hashes": [
1186 | "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2",
1187 | "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"
1188 | ],
1189 | "markers": "python_version >= '3.7'",
1190 | "version": "==1.3.1"
1191 | },
1192 | "soupsieve": {
1193 | "hashes": [
1194 | "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb",
1195 | "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"
1196 | ],
1197 | "markers": "python_version >= '3.8'",
1198 | "version": "==2.6"
1199 | },
1200 | "stack-data": {
1201 | "hashes": [
1202 | "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9",
1203 | "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"
1204 | ],
1205 | "version": "==0.6.3"
1206 | },
1207 | "terminado": {
1208 | "hashes": [
1209 | "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0",
1210 | "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"
1211 | ],
1212 | "markers": "python_version >= '3.8'",
1213 | "version": "==0.18.1"
1214 | },
1215 | "tinycss2": {
1216 | "hashes": [
1217 | "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7",
1218 | "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"
1219 | ],
1220 | "markers": "python_version >= '3.8'",
1221 | "version": "==1.4.0"
1222 | },
1223 | "tomli": {
1224 | "hashes": [
1225 | "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6",
1226 | "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd",
1227 | "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c",
1228 | "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b",
1229 | "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8",
1230 | "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6",
1231 | "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77",
1232 | "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff",
1233 | "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea",
1234 | "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192",
1235 | "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249",
1236 | "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee",
1237 | "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4",
1238 | "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98",
1239 | "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8",
1240 | "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4",
1241 | "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281",
1242 | "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744",
1243 | "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69",
1244 | "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13",
1245 | "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140",
1246 | "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e",
1247 | "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e",
1248 | "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc",
1249 | "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff",
1250 | "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec",
1251 | "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2",
1252 | "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222",
1253 | "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106",
1254 | "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272",
1255 | "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a",
1256 | "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"
1257 | ],
1258 | "markers": "python_version >= '3.8'",
1259 | "version": "==2.2.1"
1260 | },
1261 | "tornado": {
1262 | "hashes": [
1263 | "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803",
1264 | "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec",
1265 | "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482",
1266 | "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634",
1267 | "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38",
1268 | "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b",
1269 | "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c",
1270 | "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf",
1271 | "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946",
1272 | "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73",
1273 | "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"
1274 | ],
1275 | "markers": "python_version >= '3.8'",
1276 | "version": "==6.4.2"
1277 | },
1278 | "traitlets": {
1279 | "hashes": [
1280 | "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7",
1281 | "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"
1282 | ],
1283 | "markers": "python_version >= '3.8'",
1284 | "version": "==5.14.3"
1285 | },
1286 | "types-python-dateutil": {
1287 | "hashes": [
1288 | "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb",
1289 | "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"
1290 | ],
1291 | "markers": "python_version >= '3.8'",
1292 | "version": "==2.9.0.20241206"
1293 | },
1294 | "typing-extensions": {
1295 | "hashes": [
1296 | "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
1297 | "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
1298 | ],
1299 | "markers": "python_version >= '3.8'",
1300 | "version": "==4.12.2"
1301 | },
1302 | "uri-template": {
1303 | "hashes": [
1304 | "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7",
1305 | "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"
1306 | ],
1307 | "markers": "python_version >= '3.7'",
1308 | "version": "==1.3.0"
1309 | },
1310 | "urllib3": {
1311 | "hashes": [
1312 | "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
1313 | "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
1314 | ],
1315 | "markers": "python_version >= '3.9'",
1316 | "version": "==2.3.0"
1317 | },
1318 | "wcwidth": {
1319 | "hashes": [
1320 | "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859",
1321 | "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"
1322 | ],
1323 | "version": "==0.2.13"
1324 | },
1325 | "webcolors": {
1326 | "hashes": [
1327 | "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9",
1328 | "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"
1329 | ],
1330 | "markers": "python_version >= '3.9'",
1331 | "version": "==24.11.1"
1332 | },
1333 | "webencodings": {
1334 | "hashes": [
1335 | "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
1336 | "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
1337 | ],
1338 | "version": "==0.5.1"
1339 | },
1340 | "websocket-client": {
1341 | "hashes": [
1342 | "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526",
1343 | "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"
1344 | ],
1345 | "markers": "python_version >= '3.8'",
1346 | "version": "==1.8.0"
1347 | },
1348 | "zsh-jupyter-kernel": {
1349 | "editable": true,
1350 | "path": "."
1351 | }
1352 | },
1353 | "develop": {}
1354 | }
1355 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zsh kernel for Jupyter
2 |
3 | 
4 |
5 | a simple Z Shell Jupyter kernel powered by Python, IPyKernel, Pexpect,
6 | and enthusiasm — turn your scripts into notebooks!
7 |
8 | ## installation
9 |
10 | [PyPi](https://pypi.org/project/zsh-jupyter-kernel/):
11 |
12 | Pip
13 | ```sh
14 | python -m pip install zsh_jupyter_kernel
15 | ```
16 |
17 | Pipenv
18 | ```sh
19 | pipenv install zsh_jupyter_kernel
20 | ```
21 |
22 | ### install kernel file
23 |
24 | see the help command for details
25 | ```sh
26 | python -m zsh_jupyter_kernel.install --help
27 | ```
28 |
--------------------------------------------------------------------------------
/demos/example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "name": "stdout",
10 | "output_type": "stream",
11 | "text": [
12 | " ___ _ ___ _ ____ ___ \n",
13 | " / / |__ (_)_ __ / / |__ __ _ ___| |__ | ___|/ _ \\ \n",
14 | " / /| '_ \\| | '_ \\ / /| '_ \\ / _` / __| '_ \\ |___ \\ (_) |\n",
15 | " / / | |_) | | | | |/ / | |_) | (_| \\__ \\ | | | ___) \\__, |\n",
16 | "/_/ |_.__/|_|_| |_/_/ |_.__/ \\__,_|___/_| |_| |____(_)/_/ \n",
17 | " \n"
18 | ]
19 | }
20 | ],
21 | "source": [
22 | "figlet $SHELL $ZSH_VERSION"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": 2,
28 | "metadata": {},
29 | "outputs": [
30 | {
31 | "name": "stdout",
32 | "output_type": "stream",
33 | "text": [
34 | "--------------------------------------------------------------------------------\n"
35 | ]
36 | }
37 | ],
38 | "source": [
39 | "function hr { printf \"%-*s\\n\" $(tput cols) | tr ' ' '-' }\n",
40 | "hr"
41 | ]
42 | },
43 | {
44 | "cell_type": "code",
45 | "execution_count": 3,
46 | "metadata": {},
47 | "outputs": [
48 | {
49 | "name": "stdout",
50 | "output_type": "stream",
51 | "text": [
52 | "\u001b[33m47817e2\u001b[m\u001b[33m (\u001b[m\u001b[1;36mHEAD\u001b[m\u001b[33m -> \u001b[m\u001b[1;32mmaster\u001b[m\u001b[33m)\u001b[m major cleanup\n",
53 | "\u001b[33m3a49d9c\u001b[m minor reformat refactor\n",
54 | "\u001b[33m1bb6fe6\u001b[m\u001b[33m (\u001b[m\u001b[1;31morigin/master\u001b[m\u001b[33m, \u001b[m\u001b[1;31morigin/HEAD\u001b[m\u001b[33m)\u001b[m Fix bug on copying logo files\n",
55 | "--------------------------------------------------------------------------------\n",
56 | " \u001b[31mM\u001b[m ../.gitignore\n",
57 | " \u001b[31mM\u001b[m ../CONTRIBUTING.md\n",
58 | " \u001b[31mM\u001b[m ../Pipfile.lock\n",
59 | " \u001b[31mM\u001b[m ../README.md\n",
60 | "\u001b[32mR\u001b[m ../build-publish.md -> ../docs/build-install-publish.md\n",
61 | "\u001b[32mA\u001b[m\u001b[31mM\u001b[m ../docs/readme.md\n",
62 | "\u001b[32mR\u001b[m\u001b[31mM\u001b[m ../todo.md -> ../docs/todo.md\n",
63 | " \u001b[31mD\u001b[m ../example.ipynb\n",
64 | " \u001b[31mM\u001b[m ../pyproject.toml\n",
65 | " \u001b[31mD\u001b[m ../tests/sandbox.zsh.ipynb\n",
66 | " \u001b[31mM\u001b[m ../tests/zsh_kernel_test.py\n",
67 | " \u001b[31mM\u001b[m ../tools/install-pipenv.sh\n",
68 | "\u001b[32mD\u001b[m ../zsh_jupyter_kernel/LICENSE.txt\n",
69 | "\u001b[32mD\u001b[m ../zsh_jupyter_kernel/README.md\n",
70 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/__init__.py\n",
71 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/config.py\n",
72 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/fun.py\n",
73 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/install.py\n",
74 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/kernel.py\n",
75 | " \u001b[31mM\u001b[m ../zsh_jupyter_kernel/reference.md\n",
76 | "\u001b[32mD\u001b[m ../zsh_jupyter_kernel/version\n",
77 | "\u001b[32mA\u001b[m\u001b[31mM\u001b[m ../zsh_jupyter_kernel/version.txt\n",
78 | "\u001b[31m??\u001b[m ./\n",
79 | "--------------------------------------------------------------------------------\n",
80 | "\u001b[1;33mexample.ipynb\u001b[0m \u001b[1;33msandbox.zsh.ipynb\u001b[0m\n"
81 | ]
82 | }
83 | ],
84 | "source": [
85 | "git --no-pager log --oneline ...HEAD~3\n",
86 | "hr\n",
87 | "git status --short\n",
88 | "hr\n",
89 | "git ls-files src/zsh_jupyter_kernel | xargs exa"
90 | ]
91 | },
92 | {
93 | "cell_type": "code",
94 | "execution_count": 4,
95 | "metadata": {
96 | "tags": []
97 | },
98 | "outputs": [
99 | {
100 | "name": "stdout",
101 | "output_type": "stream",
102 | "text": [
103 | "Weather report: Chernivtsi\n",
104 | "\n",
105 | " \u001b[38;5;226m \\ / \u001b[0m Sunny\n",
106 | " \u001b[38;5;226m .-. \u001b[0m \u001b[38;5;049m+3\u001b[0m(\u001b[38;5;049m2\u001b[0m) °C\u001b[0m \n",
107 | " \u001b[38;5;226m ― ( ) ― \u001b[0m \u001b[1m↗\u001b[0m \u001b[38;5;118m4\u001b[0m km/h\u001b[0m \n",
108 | " \u001b[38;5;226m `-’ \u001b[0m 10 km\u001b[0m \n",
109 | " \u001b[38;5;226m / \\ \u001b[0m 0.0 mm\u001b[0m \n"
110 | ]
111 | }
112 | ],
113 | "source": [
114 | "curl -s wttr.in/Chernivtsi | head -7"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "execution_count": null,
120 | "metadata": {},
121 | "outputs": [],
122 | "source": []
123 | }
124 | ],
125 | "metadata": {
126 | "kernelspec": {
127 | "display_name": "Z shell (dev)",
128 | "language": "zsh",
129 | "name": "zsh-dev"
130 | },
131 | "language_info": {
132 | "codemirror_mode": "shell",
133 | "file_extension": ".zsh",
134 | "mimetype": "text/x-zsh",
135 | "name": "zsh",
136 | "pygments_lexer": "shell",
137 | "version": "5.7.1"
138 | }
139 | },
140 | "nbformat": 4,
141 | "nbformat_minor": 4
142 | }
143 |
--------------------------------------------------------------------------------
/demos/sandbox.zsh.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "id": "598ef7eb-8877-4586-a7f1-add8de665038",
7 | "metadata": {},
8 | "outputs": [
9 | {
10 | "name": "stdout",
11 | "output_type": "stream",
12 | "text": [
13 | "1\n",
14 | "2\n",
15 | "3\n"
16 | ]
17 | }
18 | ],
19 | "source": [
20 | "print 1\n",
21 | "echo 2\n",
22 | "<<< 3"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": 2,
28 | "id": "eb3060f6-21aa-4947-94de-233fef1481ea",
29 | "metadata": {},
30 | "outputs": [
31 | {
32 | "name": "stdout",
33 | "output_type": "stream",
34 | "text": [
35 | "example.ipynb sandbox.zsh.ipynb\n"
36 | ]
37 | }
38 | ],
39 | "source": [
40 | "ls"
41 | ]
42 | },
43 | {
44 | "cell_type": "code",
45 | "execution_count": null,
46 | "id": "ba302c08-17ba-4863-a97d-3bc8236a4f83",
47 | "metadata": {},
48 | "outputs": [],
49 | "source": []
50 | }
51 | ],
52 | "metadata": {
53 | "kernelspec": {
54 | "display_name": "Z shell (dev)",
55 | "language": "zsh",
56 | "name": "zsh-dev"
57 | },
58 | "language_info": {
59 | "codemirror_mode": "shell",
60 | "file_extension": ".zsh",
61 | "mimetype": "text/x-zsh",
62 | "name": "zsh",
63 | "pygments_lexer": "shell",
64 | "version": "5.7.1"
65 | }
66 | },
67 | "nbformat": 4,
68 | "nbformat_minor": 5
69 | }
70 |
--------------------------------------------------------------------------------
/docs/build-install-publish.md:
--------------------------------------------------------------------------------
1 | https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools
2 |
3 | ```zsh
4 | python -m pip install setuptools
5 | python -m pip install build
6 | python -m pip install twine
7 |
8 | python -m build
9 |
10 | twine upload --repository testpypi dist/*
11 |
12 | pip install --index-url https://test.pypi.org/simple/ zsh-jupyter-kernel
13 | python -m zsh_jupyter_kernel.install --sys-prefix
14 |
15 | #version=`cat version`
16 | twine upload \
17 | dist/zsh-jupyter-kernel-$version.tar.gz \
18 | dist/zsh_jupyter_kernel-$version-py3-none-any.whl
19 | ```
20 |
--------------------------------------------------------------------------------
/docs/readme.md:
--------------------------------------------------------------------------------
1 | # Zsh kernel for Jupyter
2 |
3 | 
4 |
5 | a simple Z Shell Jupyter kernel powered by Python, IPyKernel, Pexpect,
6 | and enthusiasm — turn your scripts into notebooks!
7 |
8 | ## technical details
9 |
10 | the kernel launches Zsh as if it was a regular process launched from your
11 | terminal with a few minor settings to make sure it works with Jupyter. there is
12 | slight chance it wont work with super complicated zshrc setups, but it works
13 | with majority of configs including oh-my-zsh.
14 |
15 | ### code execution
16 |
17 | the kernel configures Zsh prompt string to its own custom value.
18 | when a user requests a cell execution, the code is sent to the kernel.
19 | then the kernel puts the frontend on hold, sends the code to Zsh process, and
20 | waits for the prompt string to release the frontend and let the user request
21 | more code execution.
22 |
23 | ### code completion
24 |
25 | 
26 |
27 | code completion is powered by quite a non-trivial script that involves multiple
28 | steps, including spawning another temporary Zsh process and capturing completion
29 | options into a data structure that jupyter frontend understands.
30 |
31 | ### code inspection
32 |
33 | 
34 |
35 | code inspection is done by `man --pager ul` which sends the whole man page to
36 | the frontend.
37 |
38 | ### code completeness
39 |
40 | code completeness is checked with the temporary Zsh process and help of `EXEC`
41 | Zsh option, which allows switching off code execution and simply check if the
42 | code is complete using the exit code of the Zsh process itself.
43 |
44 | ### stderr
45 |
46 | stderr content is just sent to the front-end as regular stdout the same way it
47 | is in a terminal.
48 |
49 | ### stdin
50 |
51 | stdin is not supported because of the execution system when a process spawned by
52 | a user waits for stdin, there is no way to detect it.
53 |
54 | jupyter is a request-reply system, and Zsh as a shell that constantly receives
55 | input and prints whatever current processes want to output. there is no clear
56 | start and end of a code execution in a shell unlike in jupyter system: a
57 | front-end sends a code from a cell to a kernel and waits until the kernel sends
58 | the full output back.
59 |
60 | because of these two different ways of interacting with user Zsh jupyter kernel
61 | cannot process stdin in a way python kernel does on `input()`, meaning you will
62 | not be able to enter a sudo password or answer y/n to prompts or use a pager
63 | like less. when a spawned program waits for a user input, you will need to
64 | interrupt the kernel and use any options which do not require input.
65 |
--------------------------------------------------------------------------------
/docs/reference.md:
--------------------------------------------------------------------------------
1 | ## Reference
2 | - pexpect-spawn: https://pexpect.readthedocs.io/en/stable/api/pexpect.html#spawn-class
3 | - pexpect-spawn-timeout: https://pexpect.readthedocs.io/en/stable/api/pexpect.html?highlight=timeout#spawn-class
4 | - codecs: https://docs.python.org/3/library/codecs.html
5 | - logging: https://docs.python-guide.org/writing/logging/
6 | - zsh-options: https://linux.die.net/man/1/zshoptions
7 | - zsh-functions: http://zsh.sourceforge.net/Doc/Release/Functions.html
8 | - zsh-bracketed-paste: https://archive.zhimingwang.org/blog/2015-09-21-zsh-51-and-bracketed-paste.html
9 | - zsh-prompts: https://jlk.fjfi.cvut.cz/arch/manpages/man/zshparam.1#PARAMETERS_USED_BY_THE_SHELL
10 | - zsh-hooks: http://zsh.sourceforge.net/Doc/Release/User-Contributions.html
11 | - Section "Manipulating Hook Functions"
12 | Different plugins (e.g. oh-my-zsh with custom themes) use `precmd`/`preexec` hooks to set prompts. `add-zsh-hook -D ` allows to delete all assosiated functions from a hook.
13 | - interrupt: https://jupyter-client.readthedocs.io/en/latest/messaging.html#kernel-interrupt
14 | - kernel-specs: https://jupyter-client.readthedocs.io/en/latest/kernels.html#kernelspecs
15 |
--------------------------------------------------------------------------------
/docs/todo.md:
--------------------------------------------------------------------------------
1 | - history (for frontend)
2 | - rich html output for things like images and tables
3 | - stored and referenceable outputs
4 | - there are some setup zsh commands which need to be executed on kernel startup and they are polluting user zsh history
5 | there might be a way to exclude them from the history record
6 | - logos with transparency
7 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "zsh-jupyter-kernel"
3 | dynamic = ["version"]
4 | requires-python = ">=3.10"
5 | description = "Z shell kernel for Project Jupyter"
6 | readme = "README.md"
7 | license = { file = "LICENSE.md" }
8 | authors = [{ name = "Danylo Dubinin", email = "danylo.dubinin@gmail.com" }]
9 | keywords = ["jupyter", "zsh", "shell"]
10 | classifiers = [
11 | "Development Status :: 5 - Production/Stable",
12 | "Framework :: Jupyter",
13 | "Intended Audience :: Developers",
14 | "Intended Audience :: Science/Research",
15 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
16 | "Operating System :: OS Independent",
17 | "Programming Language :: Python :: 3.12",
18 | "Programming Language :: Unix Shell",
19 | "Topic :: System :: Shells",
20 | ]
21 | dependencies = ["ipykernel", "jupyter_client", "pexpect"]
22 | [project.urls]
23 | "Homepage" = "https://github.com/dahn-zk/zsh-jupyter-kernel"
24 | "Repository" = "https://github.com/dahn-zk/zsh-jupyter-kernel.git"
25 | "Changelog" = "https://github.com/dahn-zk/zsh-jupyter-kernel/blob/master/CHANGELOG.md"
26 |
27 | [build-system]
28 | requires = ["setuptools >= 61.0"]
29 | build-backend = "setuptools.build_meta"
30 | [tool.setuptools]
31 | packages = ["zsh_jupyter_kernel"]
32 | [tool.setuptools.dynamic]
33 | version = { file = "zsh_jupyter_kernel/version.txt" }
34 | [tool.setuptools.package-data]
35 | zsh_jupyter_kernel = ["version.txt", "capture.zsh", "banner.txt", "logo.png",
36 | "logo-32x32.png", "logo-64x64.png"]
--------------------------------------------------------------------------------
/screenshots/completion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/screenshots/completion.png
--------------------------------------------------------------------------------
/screenshots/console.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/screenshots/console.png
--------------------------------------------------------------------------------
/screenshots/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/screenshots/example.png
--------------------------------------------------------------------------------
/screenshots/inspection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/screenshots/inspection.png
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/tests/__init__.py
--------------------------------------------------------------------------------
/tests/fun_test.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase, main
2 |
3 | from zsh_jupyter_kernel.fun import find_word_at_pos
4 |
5 | class fun_test(TestCase):
6 |
7 | def test_get_word_at_pos(self):
8 | text = "non! autem~42? [ut]"
9 | t = [
10 | "non", "non", "non", "non",
11 | "",
12 | "autem", "autem", "autem", "autem", "autem", "autem",
13 | "42?", "42?", "42?", "42?"
14 | "", "",
15 | "ut", "ut",
16 | ]
17 | for i, a in enumerate(t):
18 | self.assertEqual(a, find_word_at_pos(text, pos = i), f"pos = {i}")
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/tests/msgspec_v5.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from jsonschema import Draft4Validator, ValidationError
4 |
5 | protocol_version = (5, 1)
6 |
7 | # These fragments will be wrapped in the boilerplate for a valid JSON schema.
8 | # We also add a default 'required' containing all keys.
9 | schema_fragments = {}
10 |
11 |
12 | def get_msg_content_validator(msg_type, version_minor):
13 | frag = schema_fragments[msg_type]
14 | schema = {
15 | "$schema": "http://json-schema.org/draft-04/schema#",
16 | "description": f"{msg_type} message contents schema",
17 | "type": "object",
18 | "properties": {},
19 | "additionalProperties": version_minor > protocol_version[1],
20 | }
21 | schema.update(frag)
22 | if "required" not in schema:
23 | # Require all keys by default
24 | schema["required"] = sorted(schema["properties"].keys())
25 |
26 | return Draft4Validator(schema)
27 |
28 |
29 | header_part = {
30 | "type": "object",
31 | "properties": {
32 | "msg_id": {"type": "string"},
33 | "username": {"type": "string"},
34 | "session": {"type": "string"},
35 | # TODO - this is parsed to a datetime before we get it:
36 | "date": {}, # {"type": "string"},
37 | "msg_type": {"type": "string"},
38 | "version": {"type": "string"},
39 | },
40 | "required": ["msg_id", "username", "session", "date", "msg_type", "version"],
41 | }
42 |
43 | msg_schema = {
44 | "$schema": "http://json-schema.org/draft-04/schema#",
45 | "description": "Jupyter message structure schema",
46 | "type": "object",
47 | "properties": {
48 | "header": header_part,
49 | "parent_header": {"type": "object"},
50 | "metadata": {"type": "object"},
51 | "content": {"type": "object"}, # Checked separately
52 | "buffers": {"type": "array"},
53 | },
54 | "required": ["header", "parent_header", "metadata", "content"],
55 | }
56 | msg_structure_validator = Draft4Validator(msg_schema)
57 |
58 |
59 | def get_error_reply_validator(version_minor):
60 | return Draft4Validator(
61 | {
62 | "$schema": "http://json-schema.org/draft-04/schema#",
63 | "description": "Jupyter 'error' reply schema",
64 | "type": "object",
65 | "properties": {
66 | "status": {"const": "error"},
67 | "ename": {"type": "string"},
68 | "evalue": {"type": "string"},
69 | "traceback": {"type": "array", "items": {"type": "string"}},
70 | },
71 | "required": ["status", "ename", "evalue", "traceback"],
72 | "additionalProperties": version_minor > protocol_version[1],
73 | }
74 | )
75 |
76 |
77 | def get_abort_reply_validator(version_minor):
78 | return Draft4Validator(
79 | {
80 | "$schema": "http://json-schema.org/draft-04/schema#",
81 | "description": "Jupyter 'abort' reply schema",
82 | "type": "object",
83 | "properties": {
84 | "status": {"const": "error"},
85 | "ename": {"type": "string"},
86 | "evalue": {"type": "string"},
87 | "traceback": {"type": "list", "items": {"type": "string"}},
88 | },
89 | "required": ["status", "ename", "evalue", "traceback"],
90 | "additionalProperties": version_minor > protocol_version[1],
91 | }
92 | )
93 |
94 |
95 | reply_msgs_using_status = {
96 | "execute_reply",
97 | "inspect_reply",
98 | "complete_reply",
99 | "history_reply",
100 | "connect_reply",
101 | "comm_info_reply",
102 | "kernel_info_reply",
103 | "shutdown_reply",
104 | "interrupt_reply",
105 | }
106 |
107 |
108 | def validate_message(msg, msg_type=None, parent_id=None):
109 | msg_structure_validator.validate(msg)
110 |
111 | msg_version_s = msg["header"]["version"]
112 | m = re.match(r"(\d+)\.(\d+)", msg_version_s)
113 | if not m:
114 | raise ValidationError("Version {} not like 'x.y'")
115 | version_minor = int(m.group(2))
116 |
117 | if msg_type is not None:
118 | if msg["header"]["msg_type"] != msg_type:
119 | raise ValidationError(
120 | "Message type {!r} != {!r}".format(msg["header"]["msg_type"], msg_type)
121 | )
122 | else:
123 | msg_type = msg["header"]["msg_type"]
124 |
125 | # Check for unexpected fields, unless it's a newer protocol version
126 | if version_minor <= protocol_version[1]:
127 | unx_top = set(msg) - set(msg_schema["properties"])
128 | if unx_top:
129 | raise ValidationError(f"Unexpected keys: {unx_top}")
130 |
131 | unx_header = set(msg["header"]) - set(header_part["properties"])
132 | if unx_header:
133 | raise ValidationError(f"Unexpected keys in header: {unx_header}")
134 |
135 | # Check the parent id
136 | if "reply" in msg_type and parent_id and msg["parent_header"]["msg_id"] != parent_id:
137 | raise ValidationError("Parent header does not match expected")
138 |
139 | if msg_type in reply_msgs_using_status:
140 | # Most _reply messages have common 'error' and 'abort' structures
141 | try:
142 | status = msg["content"]["status"]
143 | except KeyError as e:
144 | raise ValidationError(str(e))
145 | if status == "error":
146 | content_vdor = get_error_reply_validator(version_minor)
147 | elif status == "abort":
148 | content_vdor = get_abort_reply_validator(version_minor)
149 | elif status == "ok":
150 | content_vdor = get_msg_content_validator(msg_type, version_minor)
151 | else:
152 | raise ValidationError(f"status {status!r} should be ok/error/abort")
153 | else:
154 | content_vdor = get_msg_content_validator(msg_type, version_minor)
155 |
156 | content_vdor.validate(msg["content"])
157 |
158 |
159 | # Shell messages ----------------------------------------------
160 |
161 | schema_fragments["execute_request"] = {
162 | "properties": {
163 | "code": {"type": "string"},
164 | "silent": {"type": "boolean"},
165 | "store_history": {"type": "boolean"},
166 | "user_expressions": {"type": "object"},
167 | "allow_stdin": {"type": "boolean"},
168 | "stop_on_error": {"type": "boolean"},
169 | }
170 | }
171 |
172 | schema_fragments["execute_reply"] = {
173 | "properties": {
174 | # statuses 'error' and 'abort' change the structure, so check separately
175 | "status": {"const": "ok"},
176 | "execution_count": {"type": "number"},
177 | "payload": {
178 | "type": "array",
179 | "items": {
180 | "type": "object",
181 | "properties": {"source": {"type": "string"}},
182 | "additionalProperties": True,
183 | },
184 | },
185 | "user_expressions": {"type": "object"},
186 | },
187 | "required": ["status", "execution_count"],
188 | }
189 |
190 | schema_fragments["inspect_request"] = {
191 | "properties": {
192 | "code": {"type": "string"},
193 | "cursor_pos": {"type": "number"},
194 | "detail_level": {"enum": [0, 1]},
195 | }
196 | }
197 |
198 | schema_fragments["inspect_reply"] = {
199 | "properties": {
200 | # statuses 'error' and 'abort' change the structure, so check separately
201 | "status": {"const": "ok"},
202 | "found": {"type": "boolean"},
203 | "data": {"type": "object"},
204 | "metadata": {"type": "object"},
205 | }
206 | }
207 |
208 | schema_fragments["complete_request"] = {
209 | "properties": {
210 | "code": {"type": "string"},
211 | "cursor_pos": {"type": "number"},
212 | }
213 | }
214 |
215 | schema_fragments["complete_reply"] = {
216 | "properties": {
217 | # statuses 'error' and 'abort' change the structure, so check separately
218 | "status": {"const": "ok"},
219 | "matches": {"type": "array", "items": {"type": "string"}},
220 | "cursor_start": {"type": "number"},
221 | "cursor_end": {"type": "number"},
222 | "metadata": {"type": "object"},
223 | }
224 | }
225 |
226 | schema_fragments["history_request"] = {
227 | "properties": {
228 | "output": {"type": "boolean"},
229 | "raw": {"type": "boolean"},
230 | "hist_access_type": {"enum": ["range", "tail", "search"]},
231 | "session": {"type": "number"},
232 | "start": {"type": "number"},
233 | "stop": {"type": "number"},
234 | "n": {"type": "number"},
235 | "pattern": {"type": "string"},
236 | "unique": {"type": "boolean"},
237 | },
238 | "required": ["output", "raw", "hist_access_type"],
239 | }
240 |
241 | schema_fragments["history_reply"] = {
242 | "properties": {
243 | "status": {"const": "ok"},
244 | "history": {"type": "array", "items": {"minItems": 3, "maxItems": 3}},
245 | }
246 | }
247 |
248 | schema_fragments["is_complete_request"] = {
249 | "properties": {
250 | "code": {"type": "string"},
251 | }
252 | }
253 |
254 | schema_fragments["is_complete_reply"] = {
255 | "properties": {
256 | "status": {"enum": ["complete", "incomplete", "invalid", "unknown"]},
257 | "indent": {"type": "string"},
258 | },
259 | "required": ["status"],
260 | }
261 |
262 | # NB connect_request is deprecated
263 | schema_fragments["connect_request"] = {"properties": {}}
264 |
265 | schema_fragments["connect_reply"] = {
266 | "properties": {
267 | "shell_port": {"type": "number"},
268 | "iopub_port": {"type": "number"},
269 | "stdin_port": {"type": "number"},
270 | "hb_port": {"type": "number"},
271 | "control_port": {"type": "number"},
272 | }
273 | }
274 |
275 | schema_fragments["comm_info_request"] = {
276 | "properties": {
277 | "target_name": {"type": "string"},
278 | },
279 | "required": [],
280 | }
281 |
282 | schema_fragments["comm_info_reply"] = {
283 | "properties": {
284 | # statuses 'error' and 'abort' change the structure, so check separately
285 | "status": {"const": "ok"},
286 | "comms": {"type": "object"},
287 | }
288 | }
289 |
290 | schema_fragments["kernel_info_request"] = {"properties": {}}
291 |
292 | schema_fragments["kernel_info_reply"] = {
293 | "properties": {
294 | # statuses 'error' and 'abort' change the structure, so check separately
295 | "status": {"const": "ok"},
296 | "protocol_version": {"type": "string"},
297 | "implementation": {"type": "string"},
298 | "implementation_version": {"type": "string"},
299 | "language_info": {"type": "object"},
300 | "banner": {"type": "string"},
301 | "debugger": {"type": "boolean"},
302 | "help_links": {
303 | "type": "array",
304 | "items": {
305 | "type": "object",
306 | "properties": {"text": {"type": "string"}, "url": {"type": "string"}},
307 | },
308 | },
309 | },
310 | "required": ["status", "protocol_version", "implementation", "language_info", "banner"],
311 | }
312 |
313 | schema_fragments["shutdown_request"] = {
314 | "properties": {
315 | "restart": {"type": "boolean"},
316 | }
317 | }
318 |
319 | schema_fragments["shutdown_reply"] = {
320 | "properties": {
321 | # statuses 'error' and 'abort' change the structure, so check separately
322 | "status": {"const": "ok"},
323 | "restart": {"type": "boolean"},
324 | }
325 | }
326 |
327 | schema_fragments["interrupt_request"] = {"properties": {}}
328 | schema_fragments["interrupt_reply"] = {
329 | "properties": {
330 | # statuses 'error' and 'abort' change the structure, so check separately
331 | "status": {"const": "ok"},
332 | }
333 | }
334 |
335 | # IOPub messages ----------------------------------------------
336 |
337 | mime_data = {
338 | "type": "object",
339 | "patternProperties": {r"^[\w\-\+\.]+/[\w\-\+\.]+$": {}},
340 | "additionalProperties": False,
341 | }
342 |
343 | schema_fragments["stream"] = {
344 | "properties": {
345 | "name": {"enum": ["stdout", "stderr"]},
346 | "text": {"type": "string"},
347 | }
348 | }
349 |
350 | schema_fragments["display_data"] = {
351 | "properties": {
352 | "data": mime_data,
353 | "metadata": {"type": "object"},
354 | "transient": {"type": "object"},
355 | },
356 | "required": ["data", "metadata"],
357 | }
358 |
359 | schema_fragments["update_display_data"] = {
360 | "properties": {
361 | "data": mime_data,
362 | "metadata": {"type": "object"},
363 | "transient": {"type": "object"},
364 | }
365 | }
366 |
367 | schema_fragments["execute_result"] = {
368 | "properties": {
369 | "execution_count": {"type": "number"},
370 | "data": mime_data,
371 | "metadata": {"type": "object"},
372 | "transient": {"type": "object"},
373 | },
374 | "required": ["execution_count", "data", "metadata"],
375 | }
376 |
377 | schema_fragments["clear_output"] = {
378 | "properties": {
379 | "wait": {"type": "boolean"},
380 | }
381 | }
382 |
383 | schema_fragments["execute_input"] = {
384 | "properties": {
385 | "code": {"type": "string"},
386 | "execution_count": {"type": "number"},
387 | }
388 | }
389 |
390 | schema_fragments["error"] = {
391 | "properties": {
392 | "ename": {"type": "string"},
393 | "evalue": {"type": "string"},
394 | "traceback": {"type": "array", "items": {"type": "string"}},
395 | }
396 | }
397 |
398 | schema_fragments["status"] = {
399 | "properties": {
400 | "execution_state": {"enum": ["busy", "idle", "starting"]},
401 | }
402 | }
403 |
404 | # Stdin messages ---------------------------------------------
405 |
406 | schema_fragments["input_request"] = {
407 | "properties": {
408 | "prompt": {"type": "string"},
409 | "password": {"type": "number"},
410 | }
411 | }
412 |
413 | schema_fragments["input_reply"] = {
414 | "properties": {
415 | "value": {"type": "string"},
416 | }
417 | }
418 |
--------------------------------------------------------------------------------
/tests/pexpect_zsh_test.py:
--------------------------------------------------------------------------------
1 | import io
2 | from unittest import TestCase, main
3 |
4 | import pexpect
5 |
6 | class Pexpect_ZSH_Test(TestCase):
7 |
8 | def test_echo(self):
9 | p = pexpect.spawn('zsh +o INTERACTIVE')
10 | p.logfile = io.BytesIO()
11 | p.sendline("echo 1")
12 | p.expect("1") # timeouts if something is wrong
13 | self.assertEqual(p.logfile.getvalue(), b"echo 1\necho 1\r\n1\r\n")
14 |
15 | if __name__ == '__main__':
16 | main()
17 |
--------------------------------------------------------------------------------
/tests/zsh_kernel_test.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | import queue
3 | import unittest
4 |
5 | import jupyter_client
6 | import jupyter_client.utils
7 |
8 | from . import msgspec_v5
9 |
10 | TIMEOUT = 1
11 |
12 | class zsh_kernel_tests(unittest.TestCase):
13 | km: jupyter_client.KernelManager
14 | kc: jupyter_client.BlockingKernelClient
15 |
16 | @classmethod
17 | def setUpClass(cls):
18 | cls.km, cls.kc = jupyter_client.manager.start_new_kernel(kernel_name = "zsh")
19 |
20 | @classmethod
21 | def tearDownClass(cls):
22 | cls.kc.stop_channels()
23 | cls.km.shutdown_kernel()
24 |
25 | def setUp(self):
26 | print()
27 |
28 | @staticmethod
29 | def run_sync(func):
30 | if inspect.iscoroutinefunction(func):
31 | return jupyter_client.utils.run_sync(func)
32 | return func
33 |
34 | def flush_channels(self):
35 | for channel in (self.kc.shell_channel, self.kc.iopub_channel):
36 | while True:
37 | try:
38 | msg = self.run_sync(channel.get_msg)(timeout = 0.1)
39 | except TypeError:
40 | msg = channel.get_msg(timeout = 0.1)
41 | except queue.Empty:
42 | break
43 | else:
44 | msgspec_v5.validate_message(msg)
45 |
46 | def execute(self, code: str, timeout = TIMEOUT, silent = False,
47 | store_history = True, stop_on_error = True):
48 | msg_id = self.kc.execute(code = code, silent = silent,
49 | store_history = store_history, stop_on_error = stop_on_error)
50 | reply = self.get_non_kernel_info_reply(timeout = timeout)
51 | msgspec_v5.validate_message(reply, "execute_reply", msg_id)
52 | busy_msg = self.run_sync(self.kc.iopub_channel.get_msg)(timeout = 1)
53 | msgspec_v5.validate_message(busy_msg, "status", msg_id)
54 | self.assertEqual(busy_msg["content"]["execution_state"], "busy")
55 | output_msgs = []
56 | while True:
57 | msg = self.run_sync(self.kc.iopub_channel.get_msg)(timeout = 0.1)
58 | msgspec_v5.validate_message(msg, msg["msg_type"], msg_id)
59 | if msg["msg_type"] == "status":
60 | self.assertEqual(msg["content"]["execution_state"], "idle")
61 | break
62 | elif msg["msg_type"] == "execute_input":
63 | self.assertEqual(msg["content"]["code"], code)
64 | continue
65 | output_msgs.append(msg)
66 | return reply, output_msgs
67 |
68 | def get_non_kernel_info_reply(self, timeout = None):
69 | while True:
70 | reply = self.kc.get_shell_msg(timeout = timeout)
71 | if reply["header"]["msg_type"] != "kernel_info_reply":
72 | return reply
73 |
74 | def check_is_complete(self, sample, status):
75 | msg_id = self.kc.is_complete(sample)
76 | reply = self.get_non_kernel_info_reply()
77 | msgspec_v5.validate_message(reply, "is_complete_reply", msg_id)
78 | if reply["content"]["status"] != status:
79 | msg = "for code sample\n {!r}\nexpected {!r}, got {!r}."
80 | raise AssertionError(msg.format(sample, status, reply["content"]["status"]))
81 |
82 | def get_history(self, execute_first, timeout = TIMEOUT, **histargs):
83 | self.flush_channels()
84 | for code in execute_first:
85 | reply, output_msgs = self.execute(code)
86 | self.flush_channels()
87 | msg_id = self.kc.history(**histargs)
88 | reply = self.get_non_kernel_info_reply(timeout = timeout)
89 | msgspec_v5.validate_message(reply, "history_reply", msg_id)
90 | return reply
91 |
92 | def test_kernel_info(self):
93 | self.flush_channels()
94 | msg_id = self.kc.kernel_info()
95 | reply = self.kc.get_shell_msg(timeout = TIMEOUT)
96 | msgspec_v5.validate_message(reply, "kernel_info_reply", msg_id)
97 | self.assertEqual(reply["content"]["language_info"]["name"], "zsh")
98 | self.assertEqual(reply["content"]["language_info"]["file_extension"], ".zsh")
99 |
100 | def test_hello_world_stdout(self):
101 | self.flush_channels()
102 | reply, output_msgs = self.execute(code = "<<< 'hello, world'")
103 | self.assertEqual(reply["content"]["status"], "ok")
104 | self.assertGreaterEqual(len(output_msgs), 1)
105 | for msg in output_msgs:
106 | if (msg["msg_type"] == "stream") and (msg["content"]["name"] == "stdout"):
107 | self.assertIn("hello, world", msg["content"]["text"])
108 | break
109 | else:
110 | self.assertTrue(False, "expected one output message of type 'stream' and 'content.name'='stdout'")
111 |
112 | def test_hello_world_stderr(self):
113 | self.flush_channels()
114 | reply, output_msgs = self.execute(code = ">&2 print 'hello, world'")
115 | self.assertEqual(reply["content"]["status"], "ok")
116 | self.assertGreaterEqual(len(output_msgs), 1)
117 | for msg in output_msgs:
118 | if (msg["msg_type"] == "stream") and (msg["content"]["name"] == "stdout"):
119 | self.assertIn("hello, world", msg["content"]["text"])
120 | break
121 | else:
122 | self.assertTrue(False, "expected one output message of type 'stream' and 'content.name'='stdout'")
123 |
124 | def test_completion(self):
125 | samples = [
126 | {
127 | "text": "prin",
128 | "matches": [
129 | "printafm",
130 | "printf",
131 | "printenv",
132 | "printf",
133 | "print",
134 | ],
135 | }
136 | ]
137 | for sample in samples:
138 | text = sample["text"]
139 | with self.subTest(text = text):
140 | msg_id = self.kc.complete(text)
141 | reply = self.get_non_kernel_info_reply(timeout = TIMEOUT)
142 | msgspec_v5.validate_message(reply, "complete_reply", msg_id)
143 | if "matches" in sample:
144 | self.assertEqual(reply["content"]["matches"], sample["matches"])
145 |
146 | def test_is_complete(self):
147 | self.flush_channels()
148 | with self.subTest(status = "complete"):
149 | for sample in [
150 | 'print complete code sample; echo "100% guarantee"',
151 | '123'
152 | ]:
153 | self.check_is_complete(sample, "complete")
154 | with self.subTest(status = "incomplete"):
155 | for sample in [
156 | "1()",
157 | "echo $((2 + 2)",
158 | # "something with open single quote '", # fixme: this fails. need to handle special chars
159 | ]:
160 | self.check_is_complete(sample, "incomplete")
161 |
--------------------------------------------------------------------------------
/tools/Dockerfile:
--------------------------------------------------------------------------------
1 | # minimal-notebook version from Feb 8, 2021
2 | FROM jupyter/minimal-notebook:016833b15ceb
3 |
4 | USER root
5 |
6 | RUN apt update
7 | RUN apt upgrade --quiet --assume-yes --no-install-recommends
8 | RUN apt install --quiet --assume-yes --no-install-recommends zsh
9 | # uncomment to test oh-my-zsh
10 | # RUN sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
11 |
12 | RUN python3 -m pip install zsh_jupyter_kernel
13 | RUN python3 -m zsh_jupyter_kernel.install --sys-prefix
14 |
15 | RUN apt install --quiet --assume-yes --no-install-recommends figlet
16 |
17 | ENV JUPYTER_RUNTIME_DIR "/tmp"
18 | CMD [ "jupyter", "notebook", "--allow-root" ]
19 |
--------------------------------------------------------------------------------
/tools/docker.zsh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 |
3 | v=$(<./src/zsh_jupyter_kernel/version)
4 | t=zsh-jupyter-kernel:$v
5 |
6 | case $1 in
7 | build)
8 | docker build --tag $t .
9 | ;;
10 | run)
11 | docker run --rm --interactive --tty \
12 | --publish=8889:8888 \
13 | --volume=$PWD/test:/home/jovyan \
14 | --name=zsh-jupyter-kernel \
15 | $t
16 | ;;
17 | esac
18 |
--------------------------------------------------------------------------------
/tools/install-pip.sh:
--------------------------------------------------------------------------------
1 | # deactivate conda environment if any
2 | conda deactivate
3 |
4 | # create virtual environment
5 | python3 -m venv venv
6 |
7 | # activate it
8 | . ./venv/bin/activate
9 |
10 | # install jupyter dependencies and notebook
11 | pip install jupyter_protocol jupyter_kernel_test jupyter-console notebook jupyterlab
12 |
13 | # install zsh kernel as editable package
14 | pip install -e ./dist/pypi/setuptools
15 |
16 | # install the kernel itself
17 | python -m zsh_jupyter_kernel.install --sys-prefix
18 |
19 | # quick test
20 | jupyter console --kernel=zsh
21 |
--------------------------------------------------------------------------------
/tools/install-pipenv.sh:
--------------------------------------------------------------------------------
1 | # deactivate conda environments if any
2 | conda deactivate
3 |
4 | # remove any pipenvs
5 | pipenv --rm
6 |
7 | # check pipenv
8 | pipenv --version; which pipenv
9 |
10 | # create environment
11 | pipenv --python 3.8
12 |
13 | # install dependencies and kernel
14 | pipenv update
15 | pipenv run python -m zsh_jupyter_kernel.install --name zsh --display-name "Z shell (dev)" --sys-prefix
16 |
17 | # quick test
18 | pipenv run jupyter console --kernel=zsh
19 |
20 | # notes:
21 | # - `pipenv uninstall --all` removes some packages which are necessary for further functioning, so don't clean environment such way
22 |
--------------------------------------------------------------------------------
/tools/jupyter-rm-zsh.zsh:
--------------------------------------------------------------------------------
1 | jupyter kernelspec list --json | jq -r '.kernelspecs | keys[]' | grep zsh | xargs jupyter kernelspec remove -y
2 |
--------------------------------------------------------------------------------
/tools/run-lab.sh:
--------------------------------------------------------------------------------
1 | pipenv run jupyter lab
2 |
--------------------------------------------------------------------------------
/tools/tag-git.zsh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 | version=`cat zsh_jupyter_kernel/version.txt`
3 | git tag -a $version -m $version
4 | git push --follow-tags
5 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = [
2 | 'config',
3 | 'kernel',
4 | ]
5 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/__main__.py:
--------------------------------------------------------------------------------
1 | from ipykernel.kernelapp import launch_new_instance
2 |
3 | from .kernel import ZshKernel
4 |
5 | launch_new_instance(kernel_class = ZshKernel)
6 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/banner.txt:
--------------------------------------------------------------------------------
1 | _____ _ _ _
2 | |__ / ___| |__ ___| | |
3 | / / / __| '_ \ / _ \ | |
4 | / /_ \__ \ | | | __/ | |
5 | /____| |___/_| |_|\___|_|_|
6 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/capture.zsh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 |
3 | # original source: https://github.com/Valodim/zsh-capture-completion
4 |
5 | zmodload zsh/zpty || { echo 'error: missing module zsh/zpty' >&2; exit 1 }
6 |
7 | # spawn shell
8 | zpty z zsh -f -i
9 |
10 | # line buffer for pty output
11 | local line
12 |
13 | setopt rcquotes
14 | () {
15 | zpty -w z source $1
16 | repeat 4; do
17 | zpty -r z line
18 | [[ $line == ok* ]] && return
19 | done
20 | echo 'error initializing.' >&2
21 | exit 2
22 | } =( <<< '
23 | # no prompt!
24 | PROMPT=
25 |
26 | # load completion system
27 | autoload compinit
28 | compinit -d ~/.zcompdump_capture
29 |
30 | # never run a command
31 | bindkey ''^M'' undefined
32 | bindkey ''^J'' undefined
33 | bindkey ''^I'' complete-word
34 |
35 | # send a line with null-byte at the end before and after completions are output
36 | null-line () {
37 | echo -E - $''\0''
38 | }
39 | compprefuncs=( null-line )
40 | comppostfuncs=( null-line exit )
41 |
42 | # never group stuff!
43 | zstyle '':completion:*'' list-grouped false
44 | # don''t insert tab when attempting completion on empty line
45 | zstyle '':completion:*'' insert-tab false
46 | # no list separator, this saves some stripping later on
47 | zstyle '':completion:*'' list-separator ''''
48 |
49 | # we use zparseopts
50 | zmodload zsh/zutil
51 |
52 | # override compadd (this our hook)
53 | compadd () {
54 |
55 | # check if any of -O, -A or -D are given
56 | if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
57 | # if that is the case, just delegate and leave
58 | builtin compadd "$@"
59 | return $?
60 | fi
61 |
62 | # ok, this concerns us!
63 | # echo -E - got this: "$@"
64 |
65 | # be careful with namespacing here, we don''t want to mess with stuff that
66 | # should be passed to compadd!
67 | typeset -a __hits __dscr __tmp
68 |
69 | # do we have a description parameter?
70 | # note we don''t use zparseopts here because of combined option parameters
71 | # with arguments like -default- confuse it.
72 | if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
73 | # next param after -d
74 | __tmp=${@[$[${@[(i)-d]}+1]]}
75 | # description can be given as an array parameter name, or inline () array
76 | if [[ $__tmp == \(* ]]; then
77 | eval "__dscr=$__tmp"
78 | else
79 | __dscr=( "${(@P)__tmp}" )
80 | fi
81 | fi
82 |
83 | # capture completions by injecting -A parameter into the compadd call.
84 | # this takes care of matching for us.
85 | builtin compadd -A __hits -D __dscr "$@"
86 |
87 | # JESUS CHRIST IT TOOK ME FOREVER TO FIGURE OUT THIS OPTION WAS SET AND WAS MESSING WITH MY SHIT HERE
88 | setopt localoptions norcexpandparam extendedglob
89 |
90 | # extract prefixes and suffixes from compadd call. we can''t do zsh''s cool
91 | # -r remove-func magic, but it''s better than nothing.
92 | typeset -A apre hpre hsuf asuf
93 | zparseopts -E P:=apre p:=hpre S:=asuf s:=hsuf
94 |
95 | # append / to directories? we are only emulating -f in a half-assed way
96 | # here, but it''s better than nothing.
97 | integer dirsuf=0
98 | # don''t be fooled by -default- >.>
99 | if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
100 | dirsuf=1
101 | fi
102 |
103 | # just drop
104 | [[ -n $__hits ]] || return
105 |
106 | # this is the point where we have all matches in $__hits and all
107 | # descriptions in $__dscr!
108 |
109 | # display all matches
110 | local dsuf dscr
111 | for i in {1..$#__hits}; do
112 |
113 | # add a dir suffix?
114 | (( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
115 | # description to be displayed afterwards
116 | (( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
117 |
118 | echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf$dscr
119 |
120 | done
121 |
122 | }
123 |
124 | # signal success!
125 | echo ok')
126 |
127 | zpty -w z "$*"$'\t'
128 |
129 | integer tog=0
130 | # read from the pty, and parse linewise
131 | while zpty -r z; do :; done | while IFS= read -r line; do
132 | if [[ $line == *$'\0\r' ]]; then
133 | (( tog++ )) && return 0 || continue
134 | fi
135 | # display between toggles
136 | (( tog )) && echo -E - $line
137 | done
138 |
139 | return 2
140 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/config.py:
--------------------------------------------------------------------------------
1 | __all__ = ['config']
2 |
3 | import os
4 | from pathlib import Path
5 | from typing import Any
6 |
7 | config: dict[str, Any] = {}
8 |
9 | path = Path(__file__)
10 |
11 | version = path.with_name('version.txt').read_text()
12 |
13 | logging_enabled = os.environ.get("ZSH_JUPYTER_KERNEL_LOGGING_ENABLED", "0") == "1"
14 | config["logging_enabled"] = logging_enabled
15 | logging_level = os.environ.get("ZSH_JUPYTER_KERNEL_LOGGING_LEVEL", "INFO")
16 | config["logging_level"] = logging_level
17 | logging_dir_path = Path(os.environ.get("ZSH_JUPYTER_KERNEL_LOGGING_PATH", path.parent / "../logs"))
18 | config["logging_dir_path"] = str(logging_dir_path)
19 | if logging_enabled:
20 | logging_dir_path.mkdir(parents = True, exist_ok = True)
21 | logging_file_path = logging_dir_path / "kernel.log"
22 | config["logging_file_path"] = str(logging_file_path)
23 | config["logging_formatter"] = "%(asctime)s | %(name)-10s | %(levelname)-6s | %(message)s"
24 |
25 | config["pexpect"] = {
26 | "encoding": "utf-8",
27 | "codec_errors": "replace", # [codecs]
28 | "timeout": None, # [pexpect-spawn-timeout]
29 | "logging_file_path": str(logging_dir_path / "pexpect.log"),
30 | }
31 |
32 | config["zsh"] = {
33 | "init_cmds": [
34 | "autoload -Uz add-zsh-hook",
35 | "add-zsh-hook -D precmd \*",
36 | "add-zsh-hook -D preexec \*",
37 | # [zsh-hooks]
38 | "precmd() {}",
39 | "preexec() {}",
40 | # [zsh-functions]
41 | ],
42 | "config_cmds": [
43 | "unset zle_bracketed_paste", # [zsh-bracketed-paste]
44 | "zle_highlight=(none)", # https://linux.die.net/man/1/zshzle
45 | ],
46 | }
47 |
48 | config["kernel"] = {
49 | "code_completion": {"cmd": str(path.with_name("capture.zsh")) + " {}"},
50 | "info": {
51 | "protocol_version": "5.3",
52 | "implementation": "ZshKernel",
53 | "implementation_version": version,
54 | "language_info": {
55 | "name": "zsh",
56 | "version": "5.7.1",
57 | "mimetype": "text/x-zsh",
58 | "file_extension": ".zsh",
59 | "pygments_lexer": "shell",
60 | "codemirror_mode": "shell",
61 | },
62 | "banner": path.with_name("banner.txt").read_text(),
63 | },
64 | }
65 |
66 | if __name__ == "__main__":
67 | import json
68 | print(json.dumps(config, indent = 4))
69 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/fun.py:
--------------------------------------------------------------------------------
1 | def find_word_at_pos(text : str, pos : int) -> str:
2 | cw = ""
3 | p = 0
4 | ic = '\n\t"' + r" |;,!@#$()<>/\'`~{}[]=+&^"
5 |
6 | while p < len(text) and p < pos:
7 | c = text[p]
8 | if c in ic:
9 | cw = ""
10 | else:
11 | cw += c
12 | p += 1
13 |
14 | while p < len(text):
15 | c = text[p]
16 | if c in ic:
17 | break
18 | else:
19 | cw += c
20 | p += 1
21 |
22 | return cw
23 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/install.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import json
3 | import os
4 | from pathlib import Path
5 | import shutil
6 | import sys
7 | import traceback
8 | from tempfile import TemporaryDirectory
9 | from typing import Literal
10 |
11 | import jupyter_client.kernelspec
12 |
13 | def main(argv = None):
14 | args = parse_args(argv)
15 | if args.prefix:
16 | install(args.name, args.display_name, custom_path_prefix = args.prefix)
17 | elif args.sys_prefix:
18 | install(args.name, args.display_name, path_prefix = 'sys')
19 | elif args.user:
20 | install(args.name, args.display_name, path_prefix = 'user')
21 | else:
22 | install(args.name, args.display_name)
23 |
24 | def install(
25 | kernel_name: str = "zsh",
26 | display_name: str = "zsh",
27 | path_prefix: Literal['user', 'sys', 'default'] = 'default',
28 | custom_path_prefix: str = None,
29 | ):
30 | try:
31 | with TemporaryDirectory() as tempd:
32 | install_(kernel_name, display_name, path_prefix, custom_path_prefix, tempd)
33 | except Exception as e:
34 | traceback.print_exception(e)
35 | print()
36 | print("sorry, an unhandled error occured.")
37 |
38 | def install_(kernel_name, display_name, path_prefix, custom_path_prefix, tempd):
39 | os.chmod(tempd, 0o755) # starts off as 700, not user readable
40 | # [kernel-specs]
41 | with open(Path(tempd) / 'kernel.json', 'w') as f:
42 | d = {
43 | "argv": [sys.executable, "-m", 'zsh_jupyter_kernel', "-f", "{connection_file}"],
44 | "display_name": display_name,
45 | "language": "zsh",
46 | "interrupt_mode": "signal",
47 | }
48 | # noinspection PyTypeChecker
49 | json.dump(d, f, indent = 4)
50 | # logos
51 | for logof in ('logo-32x32.png', 'logo-64x64.png'):
52 | src = Path(__file__).with_name(logof)
53 | dst = Path(tempd) / logof
54 | shutil.copyfile(src, dst)
55 | #
56 | user = path_prefix == 'user'
57 | prefix = None
58 | if custom_path_prefix is not None:
59 | prefix = custom_path_prefix
60 | elif path_prefix == 'sys':
61 | prefix = sys.prefix
62 | try:
63 | jupyter_client.kernelspec.install_kernel_spec(
64 | source_dir = tempd,
65 | kernel_name = kernel_name,
66 | user = user and not (custom_path_prefix is not None),
67 | prefix = prefix,
68 | )
69 | except PermissionError as e:
70 | print(e)
71 | print('sorry, you do not have appropriate permissions to install kernel in the specified location.\n'
72 | 'if you want to install in the default system-wide location, use elevated priviliges or login'
73 | 'as an administrator.\n'
74 | 'otherwise try installing in a current python environment location using --sys-prefix.\n'
75 | 'use --help to read more.')
76 | else:
77 | spec = jupyter_client.kernelspec.get_kernel_spec(kernel_name = kernel_name)
78 | print(
79 | f"installed Z shell jupyter kernel spec in {spec.resource_dir}:\n"
80 | f"""{json.dumps(dict(
81 | argv = spec.argv,
82 | env = spec.env,
83 | display_name = spec.display_name,
84 | language = spec.language,
85 | interrupt_mode = spec.interrupt_mode,
86 | metadata = spec.metadata,
87 | ), indent = 4)}"""
88 | )
89 |
90 | def install_logos(tempd):
91 | for logof in ('logo-32x32.png', 'logo-64x64.png'):
92 | src = Path(__file__).with_name(logof)
93 | dst = Path(tempd) / logof
94 | shutil.copyfile(src, dst)
95 |
96 | def is_root() -> bool:
97 | try:
98 | return os.geteuid() == 0
99 | except AttributeError:
100 | return False # assume not an admin on non-Unix platforms
101 |
102 | class ArgumentFormatter(
103 | argparse.ArgumentDefaultsHelpFormatter,
104 | argparse.RawDescriptionHelpFormatter):
105 | pass
106 |
107 | def parse_args(argv) -> argparse.Namespace:
108 | ksm = jupyter_client.kernelspec.KernelSpecManager()
109 | name = "${NAME}"
110 | # noinspection PyProtectedMember
111 | user_dir = ksm._get_destination_dir(name, user = True)
112 | # noinspection PyProtectedMember
113 | sys_dir = ksm._get_destination_dir(name, prefix = sys.prefix)
114 | # noinspection PyProtectedMember
115 | root_dir = ksm._get_destination_dir(name)
116 | ap = argparse.ArgumentParser(
117 | description = "install zsh jupyter kernel",
118 | epilog = f"the kernel will be installed in one of the locations:\n"
119 | f" by default: {root_dir}\n"
120 | f" using --sys-prefix: {sys_dir}\n"
121 | f" using --user: {user_dir}\n",
122 | formatter_class = ArgumentFormatter,
123 | )
124 | ap.add_argument(
125 | '--name',
126 | default = 'zsh',
127 | help = "directory name in the kernelspec repository. use this to specify a unique location for the kernel"
128 | " if you use more than one version, otherwise just skip it and use the default 'zsh' value."
129 | " since kernelspecs show up in urls and other places, a kernelspec is required to have a simple name,"
130 | " only containing ascii letters, ascii numbers, and the simple separators:"
131 | " - hyphen, . period, _ underscore.",
132 | )
133 | ap.add_argument(
134 | '--display-name',
135 | default = 'Z shell',
136 | help = "the kernel’s name as it should be displayed in the ui. unlike the --name used in the api,"
137 | " this can contain arbitrary unicode characters.",
138 | )
139 | ap.add_argument(
140 | '--user',
141 | action = 'store_true',
142 | help = "install to the per-user kernels registry. default if not root. ignored if --prefix is specified",
143 | )
144 | ap.add_argument(
145 | '--sys-prefix',
146 | action = 'store_true',
147 | help = "install to sys.prefix (e.g. a virtualenv, pipenv or conda env)",
148 | )
149 | ap.add_argument(
150 | '--prefix',
151 | help = "install to the given prefix.",
152 | )
153 | return ap.parse_args(argv)
154 |
155 | if __name__ == '__main__':
156 | main(sys.argv[1:])
157 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/kernel.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import logging.handlers
4 | import os
5 | import re
6 | from collections import OrderedDict as odict
7 | from typing import IO
8 |
9 | import pexpect
10 | from ipykernel.kernelbase import Kernel
11 |
12 | from .config import config
13 | from .fun import find_word_at_pos
14 |
15 | class ZshKernel(Kernel):
16 | implementation = config["kernel"]["info"]["implementation"]
17 | implementation_version = config["kernel"]["info"]["implementation_version"]
18 | banner = config["kernel"]["info"]["banner"]
19 | language_info = config["kernel"]["info"]["language_info"]
20 |
21 | protocol_version = config["kernel"]["info"]["protocol_version"]
22 |
23 | p: pexpect.spawn # [spawn]
24 |
25 | ps = odict([
26 | ("PS1", "PEXPECT_PS1 > "),
27 | ("PS2", "PEXPECT_PS2 + "),
28 | ("PS3", "PEXPECT_PS3 : "),
29 | ])
30 | ps_re = odict([
31 | ("PS1", "^PEXPECT_PS1 > "),
32 | ("PS2", r"^PEXPECT_PS2 \+ "),
33 | ("PS3", "^PEXPECT_PS3 : "),
34 | ])
35 |
36 | pexpect_logfile: IO = None
37 |
38 | log_enabled: bool
39 |
40 | def _init_log_(self, **kwargs):
41 | self.log_enabled = config["logging_enabled"]
42 | if self.log_enabled:
43 | handler = logging.handlers.WatchedFileHandler(config["logging_file_path"])
44 | formatter = logging.Formatter(config["logging_formatter"])
45 | handler.setFormatter(formatter)
46 | self.log.setLevel(config["logging_level"])
47 | self.log.addHandler(handler)
48 |
49 | def _init_spawn_(self, **kwargs):
50 | args = [
51 | "-o", "INTERACTIVE", # just to make sure
52 | "-o", "NO_ZLE", # no need for zsh line editor
53 | "-o", "NO_BEEP",
54 | "-o", "TRANSIENT_RPROMPT",
55 | "-o", "NO_PROMPT_CR",
56 | "-o", "INTERACTIVE_COMMENTS",
57 | ] # [zsh-options]
58 | if not kwargs.get("rcs", False):
59 | args.extend(["-o", "NO_RCS"])
60 | if self.log_enabled:
61 | self.pexpect_logfile = open(config["pexpect"]["logging_file_path"], "a")
62 | self.p = pexpect.spawn(
63 | "zsh",
64 | args,
65 | echo = False,
66 | encoding = config["pexpect"]["encoding"],
67 | codec_errors = config["pexpect"]["codec_errors"],
68 | timeout = config["pexpect"]["timeout"],
69 | logfile = self.pexpect_logfile,
70 | )
71 |
72 | def _init_zsh_(self, **kwargs):
73 | init_cmds = [
74 | *config["zsh"]["init_cmds"],
75 | *map(lambda kv: "{}='{}'".format(*kv), self.ps.items()),
76 | ]
77 | self.p.sendline("; ".join(init_cmds))
78 | self.p.expect_exact(self.ps["PS1"])
79 | config_cmds = [
80 | *config["zsh"]["config_cmds"],
81 | ]
82 | self.p.sendline("; ".join(config_cmds))
83 | self.p.expect_exact(self.ps["PS1"])
84 |
85 | def __init__(self, **kwargs):
86 | super().__init__(**kwargs)
87 | self._init_log_()
88 | if self.log_enabled:
89 | self.log.debug("initializing %s", json.dumps(config, indent = 4))
90 | self._init_spawn_()
91 | self._init_zsh_(**kwargs)
92 | # self.p.sendline("tty")
93 | # self.p.expect_exact(self.ps['PS1'])
94 | if self.log_enabled:
95 | self.log.debug("initialized")
96 | if self.log_enabled:
97 | self.log.debug("kwargs:" + str(kwargs))
98 |
99 | def __del__(self):
100 | try:
101 | self.pexpect_logfile.close()
102 | except AttributeError:
103 | pass
104 |
105 | def kernel_info_request(self, stream, ident, parent):
106 | content = {"status": "ok"}
107 | content.update(self.kernel_info)
108 | content.update({"protocol_version": self.protocol_version})
109 | msg = self.session.send(stream, "kernel_info_reply", content, parent, ident)
110 | if self.log_enabled:
111 | self.log.debug("info request sent: %s", msg)
112 |
113 | def _execute_line(self, line, silent):
114 | if self.log_enabled:
115 | self.log.debug("code: %s", line)
116 | self.p.sendline(line)
117 | actual = self.p.expect(list(self.ps_re.values()) + [os.linesep])
118 | if actual == 0:
119 | if self.log_enabled:
120 | self.log.debug(f"got PS1. output: {self.p.before}")
121 | if not silent:
122 | if len(self.p.before) != 0:
123 | self.send_response(self.iopub_socket,
124 | "stream", {"name": "stdout", "text": self.p.before})
125 | else:
126 | while actual == 3:
127 | if self.log_enabled:
128 | self.log.debug(f"got linesep. output: {self.p.before}")
129 | if not silent:
130 | self.send_response(self.iopub_socket,
131 | "stream", {"name": "stdout", "text": self.p.before + os.linesep})
132 | actual = self.p.expect(list(self.ps_re.values()) + [os.linesep])
133 | return actual
134 |
135 | def _execute_code(self, code, silent):
136 | actual = None
137 | for line in code.splitlines():
138 | actual = self._execute_line(line, silent)
139 | if self.log_enabled:
140 | self.log.debug(f"executed all lines. actual: {actual}")
141 | if actual in [1, 2]:
142 | self.p.sendline()
143 | # "flushing"
144 | actual = self.p.expect(list(self.ps_re.values()))
145 | while actual != 0:
146 | actual = self.p.expect(list(self.ps_re.values()) + [re.compile(".*")])
147 | if not silent:
148 | self.send_response(self.iopub_socket,
149 | "stream", {"name": "stdout", "text": self.p.before})
150 | raise ValueError("Continuation or selection prompts are not handled yet")
151 |
152 | def _get_error_response(self, exc):
153 | return {
154 | "execution_count": self.execution_count,
155 | "ename": exc.__class__.__name__,
156 | "evalue": exc.__class__.__name__,
157 | "traceback": [],
158 | # 'traceback': traceback.extract_stack(exc),
159 | }
160 |
161 | def do_execute(self, code: str, silent: bool, store_history = True,
162 | user_expressions: dict = None, **kwargs):
163 | try:
164 | self._execute_code(code, silent)
165 | except KeyboardInterrupt as exc:
166 | if self.log_enabled: self.log.debug("interrupted by user")
167 | self.p.sendintr()
168 | self.p.expect_exact(self.ps["PS1"])
169 | if not silent:
170 | self.send_response(self.iopub_socket, "stream",
171 | {"name": "stdout", "text": self.p.before})
172 | error_response = self._get_error_response(exc)
173 | return {"status": "error", **error_response}
174 | except ValueError as exc:
175 | if self.log_enabled: self.log.exception("value error")
176 | error_response = self._get_error_response(exc)
177 | self.send_response(self.iopub_socket, "error", error_response)
178 | return {"status": "error", **error_response}
179 | except pexpect.TIMEOUT as exc:
180 | if self.log_enabled: self.log.exception("timeout")
181 | error_response = self._get_error_response(exc)
182 | self.send_response(self.iopub_socket, "error", error_response)
183 | return {"status": "error", **error_response}
184 | except pexpect.EOF as exc:
185 | if self.log_enabled: self.log.exception("end of file")
186 | error_response = self._get_error_response(exc)
187 | self.send_response(self.iopub_socket, "error", error_response)
188 | return {"status": "error", **error_response}
189 |
190 | if self.log_enabled: self.log.debug(f"success {self.execution_count}")
191 | return {
192 | "status": "ok",
193 | "execution_count": self.execution_count,
194 | "payload": [],
195 | "user_expressions": {},
196 | }
197 |
198 | def do_is_complete(self, code: str):
199 | (_, exitstatus) = pexpect.run(
200 | "zsh -nc '{}'".format(code),
201 | withexitstatus = True,
202 | )
203 | if exitstatus == 0:
204 | return {"status": "complete"}
205 | elif exitstatus == 1:
206 | return {"status": "incomplete"}
207 |
208 | def do_inspect(self, code: str, cursor_pos: int, detail_level: int = 0,
209 | omit_sections = ()):
210 | word = find_word_at_pos(code, cursor_pos)
211 | if self.log_enabled:
212 | self.log.debug("inspecting: %s", word)
213 | cman = f"man --pager ul {word}"
214 | res = pexpect.run(cman).decode()
215 | return {
216 | "status": "ok",
217 | "found": True,
218 | "data": {"text/plain": res},
219 | "metadata": {},
220 | }
221 |
222 | @staticmethod
223 | def _parse_completee(code: str, cursor_pos: int) -> tuple:
224 | context = code[:cursor_pos]
225 | match = re.search(r"\S+$", context)
226 | if not match:
227 | match = re.search(r"\w+$", context)
228 | if not match:
229 | completee = ""
230 | else:
231 | completee = match.group(0)
232 | cursor_start = cursor_pos - len(completee)
233 | cursor_end = cursor_pos
234 | return context, completee, cursor_start, cursor_end
235 |
236 | def do_complete(self, code: str, cursor_pos: int):
237 | if self.log_enabled:
238 | self.log.debug("received code to complete:\n%s", code)
239 | if self.log_enabled:
240 | self.log.debug("cursor_pos=%s", cursor_pos)
241 | (context, completee, cursor_start, cursor_end) = \
242 | self._parse_completee(code, cursor_pos)
243 | if self.log_enabled:
244 | self.log.debug("parsed completee: %s",
245 | (context, completee, cursor_start, cursor_end))
246 | completion_cmd = config["kernel"]["code_completion"]["cmd"].format(context)
247 | self.p.sendline(completion_cmd)
248 | self.p.expect_exact(self.ps["PS1"])
249 | raw_completions = self.p.before.strip()
250 | if self.log_enabled:
251 | self.log.debug("got completions:\n%s", raw_completions)
252 | completions = list(filter(None, raw_completions.splitlines()))
253 | if self.log_enabled:
254 | self.log.debug("array of completions: %s", completions)
255 | matches_data = list(
256 | map(lambda x: x.split(" -- "), completions)
257 | ) # [match, description]
258 | if self.log_enabled:
259 | self.log.debug("processed matches: %s", matches_data)
260 | return {
261 | "status": "ok",
262 | "matches": [x[0] for x in matches_data],
263 | "cursor_start": cursor_start,
264 | "cursor_end": cursor_end,
265 | "metadata": {},
266 | }
267 |
268 | def send_response(self, stream, msg_or_type, content = None, ident = None,
269 | buffers = None, track = False, header = None, metadata = None, channel = None):
270 | if self.log_enabled:
271 | self.log.debug("sending response: %s", msg_or_type)
272 | super().send_response(stream, msg_or_type, content, ident, buffers, track,
273 | header, metadata, channel)
274 |
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/logo-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/zsh_jupyter_kernel/logo-32x32.png
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/logo-64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/zsh_jupyter_kernel/logo-64x64.png
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dahn-zk/zsh-jupyter-kernel/e591c27c791c9c6f8ba7296074fa9a316690426f/zsh_jupyter_kernel/logo.png
--------------------------------------------------------------------------------
/zsh_jupyter_kernel/version.txt:
--------------------------------------------------------------------------------
1 | 3.5.1
--------------------------------------------------------------------------------