├── .gitignore
├── .idea
├── .gitignore
├── conceptnet-lite.iml
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── LICENSE
├── README.md
├── conceptnet_lite
├── __init__.py
├── db.py
└── utils.py
├── docs
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── _static
│ └── erd.svg
│ ├── conceptnet_lite.rst
│ ├── conf.py
│ └── index.rst
├── examples
├── conceptnet_queries.py
└── download_and_load_dump_to_db.py
├── poetry.lock
├── pyproject.toml
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.db
2 | *.csv
3 | pip-wheel-metadata/
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | target/
75 |
76 | # Jupyter Notebook
77 | .ipynb_checkpoints
78 |
79 | # pyenv
80 | .python-version
81 |
82 | # celery beat schedule file
83 | celerybeat-schedule
84 |
85 | # SageMath parsed files
86 | *.sage.py
87 |
88 | # Environments
89 | .env
90 | .venv
91 | env/
92 | venv/
93 | ENV/
94 | env.bak/
95 | venv.bak/
96 |
97 | # Spyder project settings
98 | .spyderproject
99 | .spyproject
100 |
101 | # Rope project settings
102 | .ropeproject
103 |
104 | # mkdocs documentation
105 | /site
106 |
107 | # mypy
108 | .mypy_cache/
109 |
110 | # Created by https://www.gitignore.io/api/pycharm
111 | # Edit at https://www.gitignore.io/?templates=pycharm
112 |
113 | ### PyCharm ###
114 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
115 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
116 |
117 | # User-specific stuff
118 | .idea/**/workspace.xml
119 | .idea/**/tasks.xml
120 | .idea/**/usage.statistics.xml
121 | .idea/**/dictionaries
122 | .idea/**/shelf
123 |
124 | # Generated files
125 | .idea/**/contentModel.xml
126 |
127 | # Sensitive or high-churn files
128 | .idea/**/dataSources/
129 | .idea/**/dataSources.ids
130 | .idea/**/dataSources.local.xml
131 | .idea/**/sqlDataSources.xml
132 | .idea/**/dynamic.xml
133 | .idea/**/uiDesigner.xml
134 | .idea/**/dbnavigator.xml
135 |
136 | # Gradle
137 | .idea/**/gradle.xml
138 | .idea/**/libraries
139 |
140 | # Gradle and Maven with auto-import
141 | # When using Gradle or Maven with auto-import, you should exclude module files,
142 | # since they will be recreated, and may cause churn. Uncomment if using
143 | # auto-import.
144 | # .idea/modules.xml
145 | # .idea/*.iml
146 | # .idea/modules
147 | # *.iml
148 | # *.ipr
149 |
150 | # CMake
151 | cmake-build-*/
152 |
153 | # Mongo Explorer plugin
154 | .idea/**/mongoSettings.xml
155 |
156 | # File-based project format
157 | *.iws
158 |
159 | # IntelliJ
160 | out/
161 |
162 | # mpeltonen/sbt-idea plugin
163 | .idea_modules/
164 |
165 | # JIRA plugin
166 | atlassian-ide-plugin.xml
167 |
168 | # Cursive Clojure plugin
169 | .idea/replstate.xml
170 |
171 | # Crashlytics plugin (for Android Studio and IntelliJ)
172 | com_crashlytics_export_strings.xml
173 | crashlytics.properties
174 | crashlytics-build.properties
175 | fabric.properties
176 |
177 | # Editor-based Rest Client
178 | .idea/httpRequests
179 |
180 | # Android studio 3.1+ serialized cache file
181 | .idea/caches/build_file_checksums.ser
182 |
183 | ### PyCharm Patch ###
184 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
185 |
186 | # *.iml
187 | # modules.xml
188 | # .idea/misc.xml
189 | # *.ipr
190 |
191 | # Sonarlint plugin
192 | .idea/sonarlint
193 |
194 | # End of https://www.gitignore.io/api/pycharm
195 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Default ignored files
3 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/conceptnet-lite.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # conceptnet-lite [maintainance mode]
2 | [](https://www.apache.org/licenses/LICENSE-2.0)
3 | 
4 | [](https://pypi.org/project/conceptnet-lite/)
5 | [](http://conceptnet-lite.readthedocs.io/en/latest/)
6 |
7 | Conceptnet-lite is a Python library for working with ConceptNet offline without the need for PostgreSQL.
8 |
9 | The library comes with Apache License 2.0, and is separate from ConceptNet itself. The ConceptNet is available under [CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/) license, which also applies to the formatted database file that we provide. See [here](https://github.com/commonsense/conceptnet5/wiki/Copying-and-sharing-ConceptNet) for the list of conditions for using ConceptNet data.
10 |
11 | This is the official citation for ConceptNet if you use it in research:
12 |
13 | > Robyn Speer, Joshua Chin, and Catherine Havasi. 2017. "ConceptNet 5.5: An Open Multilingual Graph of General Knowledge." In proceedings of AAAI 31.
14 |
15 | ## Status
16 |
17 | ConceptNet was not updated since September 2021, so Conceptnet-lite won't be updated either. Please do not use it in any new projects.
18 |
19 | ## Installation
20 |
21 | To install `conceptnet-lite` use `pip`:
22 |
23 | ```shell
24 | $ pip install conceptnet-lite
25 | ```
26 |
27 | ## Connecting to the database
28 |
29 | Before you can use `conceptnet-lite`, you will need to obtain ConceptNet dabase file. You have two options: download pre-made one or build it yourself from the raw ConceptNet assertions file.
30 |
31 | ### Downloading the ConceptNet database
32 |
33 | ConceptNet releases happen once a year. You can use `conceptnet-lite` to build your own database from the raw assertions file (see below), but if there is a pre-built file it will be faster to just get that one. `conceptnet-lite` can download and unpack it to the specified folder automatically.
34 |
35 | Here is a [link](https://conceptnet-lite.fra1.cdn.digitaloceanspaces.com/conceptnet.db.zip) to a compressed database for ConceptNet 5.7. This link is used automatically if you do not supply the alternative.
36 |
37 | ```python
38 | import conceptnet_lite
39 |
40 | conceptnet_lite.connect("/path/to/conceptnet.db")
41 | ```
42 |
43 | This command both downloads the resource (our build for ConceptNet 5.7) and connects to the database. If path specified as the first argument does not exist, it will be created (unless there is a permissions problem). Note that the database file is quite large (over 9 Gb).
44 |
45 | If your internet connection is intermittent, the built-in download function may give you errors. If so, just download the file separately, unpack it to the directory of your choice and provide the path to the `.connect()` method as described below.
46 |
47 | ### Building the database for a new release.
48 |
49 | If a database file is not found in the folder specified in the `db_path` argument, `conceptnet-lite` will attempt to automatically download the raw assertions file from [here](https://github.com/commonsense/conceptnet5/wiki/Downloads) and build the database. This takes a couple of hours, so we recommend getting the pre-built file.
50 |
51 | If you provide a path, this is where the database will be built. Note that the database file is quite large (over 9 Gb). Note that you need to pass `db_download_url=None` to force the library build the database from dump.
52 |
53 | ```python
54 | import conceptnet_lite
55 |
56 | conceptnet_lite.connect("/path/to/conceptnet.db", db_download_url=None)
57 | ```
58 |
59 | If the specified does not exist, it will be created (unless there is a permissions problem). If no path is specified, and no database file is not found in the current working directory, `conceptnet-lite` will attempt to build one in the current working directory.
60 |
61 | Once the database is built, `conceptnet-lite` will connect to it automatically.
62 |
63 | ### Loading the ConceptNet database
64 |
65 | Once you have the database file, all you need to do is to pass the path to it to the `.connect()` method.
66 |
67 | ```python
68 | import conceptnet_lite
69 |
70 | conceptnet_lite.connect("/path/to/conceptnet.db")
71 | ```
72 |
73 | If no path is specified, `conceptnet-lite` will check if a database file exists in the current working directory. If it is not found, it will trigger the process of downloading the pre-built database (see above).
74 |
75 | ## Accessing concepts
76 |
77 | Concepts objects are created by looking for every entry that matches the input string exactly.
78 | If none is found, the `peewee.DoesNotExist` exception will be raised.
79 |
80 | ```python
81 | from conceptnet_lite import Label
82 |
83 | cat_concepts = Label.get(text='cat').concepts
84 | for c in cat_concepts:
85 | print(" Concept URI:", c.uri)
86 | print(" Concept text:", c.text)
87 | ```
88 | ```console
89 | Concept URI: /c/en/cat
90 | Concept text: cat
91 | Concept URI: /c/en/cat/n
92 | Concept text: cat
93 | Concept URI: /c/en/cat/n/wn/animal
94 | Concept text: cat
95 | Concept URI: /c/en/cat/n/wn/person
96 | ...
97 | ```
98 |
99 | `concept.uri` provides access to ConceptNet URIs, as described [here](https://github.com/commonsense/conceptnet5/wiki/URI-hierarchy). You can also retrieve only the text of the entry by `concept.text`.
100 |
101 | ## Working with languages
102 |
103 | You can limit the languages to search for matches. Label.get() takes an optional `language` attribute that is expected to be an instance `Language`, which in turn is created by calling `Language.get()` with `name` argument.
104 | List of available languages and their codes are described [here](https://github.com/commonsense/conceptnet5/wiki/Languages).
105 |
106 | ```python
107 | from conceptnet_lite import Label
108 |
109 | cat_concepts = Label.get(text='cat', language='en').concepts
110 | for c in cat_concepts:
111 | print(" Concept URI:", c.uri)
112 | print(" Concept text:", c.text)
113 | print(" Concept language:", c.language.name)
114 | ```
115 |
116 | ```console
117 | Concept URI: /c/en/cat
118 | Concept text: cat
119 | Concept language: en
120 | Concept URI: /c/en/cat/n
121 | Concept text: cat
122 | Concept language: en
123 | Concept URI: /c/en/cat/n/wn/animal
124 | Concept text: cat
125 | Concept language: en
126 | Concept URI: /c/en/cat/n/wn/person
127 | Concept text: cat
128 | Concept language: en
129 | ...
130 | ```
131 |
132 |
133 | ## Querying edges between concepts
134 |
135 | To retrieve the set of relations between two concepts, you need to create the concept objects (optionally specifying the language as described above). `cn.edges_between()` method retrieves all edges between the specified concepts. You can access its URI and a number of attributes, as shown below.
136 |
137 | Some ConceptNet relations are symmetrical: for example, the antonymy between *white* and *black* works both ways. Some relations are asymmetrical: e.g. the relation between *cat* and *mammal* is either hyponymy or hyperonymy, depending on the direction. The `two_way` argument lets you choose whether the query should be symmetrical or not.
138 |
139 | ```python
140 | from conceptnet_lite import Label, edges_between
141 |
142 | introvert_concepts = Label.get(text='introvert', language='en').concepts
143 | extrovert_concepts = Label.get(text='extrovert', language='en').concepts
144 | for e in edges_between(introvert_concepts, extrovert_concepts, two_way=False):
145 | print(" Edge URI:", e.uri)
146 | print(" Edge name:", e.relation.name)
147 | print(" Edge start node:", e.start.text)
148 | print(" Edge end node:", e.end.text)
149 | print(" Edge metadata:", e.etc)
150 | ```
151 | ```console
152 | Edge URI: /a/[/r/antonym/,/c/en/introvert/n/,/c/en/extrovert/]
153 | Edge name: antonym
154 | Edge start node: introvert
155 | Edge end node: extrovert
156 | Edge metadata: {'dataset': '/d/wiktionary/en', 'license': 'cc:by-sa/4.0', 'sources': [{'contributor': '/s/resource/wiktionary/en', 'process': '/s/process/wikiparsec/2'}, {'contributor': '/s/resource/wiktionary/fr', 'process': '/s/process/wikiparsec/2'}], 'weight': 2.0}
157 | ```
158 |
159 | * **e.relation.name**: the name of ConceptNet relation. Full list [here](https://github.com/commonsense/conceptnet5/wiki/Relations).
160 |
161 | * **e.start.text, e.end.text**: the source and the target concepts in the edge
162 |
163 | * **e.etc**: the ConceptNet [metadata](https://github.com/commonsense/conceptnet5/wiki/Edges) dictionary contains the source dataset, sources, weight, and license. For example, the introvert:extrovert edge for English contains the following metadata:
164 |
165 | ```json
166 | {
167 | "dataset": "/d/wiktionary/en",
168 | "license": "cc:by-sa/4.0",
169 | "sources": [{
170 | "contributor": "/s/resource/wiktionary/en",
171 | "process": "/s/process/wikiparsec/2"
172 | }, {
173 | "contributor": "/s/resource/wiktionary/fr",
174 | "process": "/s/process/wikiparsec/2"
175 | }],
176 | "weight": 2.0
177 | }
178 | ```
179 |
180 | ## Accessing all relations for a given concepts
181 |
182 | You can also retrieve all relations between a given concepts and all other concepts, with the same options as above:
183 |
184 | ```python
185 | from conceptnet_lite import Label, edges_for
186 |
187 | for e in edges_for(Label.get(text='introvert', language='en').concepts, same_language=True):
188 | print(e.start.text, "::", e.end.text, "|", e.relation.name)
189 | ```
190 | ```console
191 | extrovert :: introvert | antonym
192 | introvert :: extrovert | antonym
193 | outrovert :: introvert | antonym
194 | reflection :: introvert | at_location
195 | introverse :: introvert | derived_from
196 | introversible :: introvert | derived_from
197 | introversion :: introvert | derived_from
198 | introversion :: introvert | derived_from
199 | introversive :: introvert | derived_from
200 | introverted :: introvert | derived_from
201 | ...
202 | ```
203 |
204 | The same set of edge attributes are available for `edges_between` and `edges_for` (e.uri, e.relation.name, e.start.text, e.end.text, e.etc).
205 |
206 | Note that we have used optional argument `same_language=True`. By supplying this argument we make `edges_for` return
207 | relations, both ends of which are in the same language. If this argument is skipped it is possible to get edges to
208 | concepts in languages other than the source concepts language. For example, the same command as above with `same_language=False` will include the following in the output:
209 |
210 | ```console
211 | kääntyä_sisäänpäin :: introvert | synonym
212 | sulkeutua :: introvert | synonym
213 | sulkeutunut :: introvert | synonym
214 | introverti :: introvert | synonym
215 | asociale :: introvert | synonym
216 | introverso :: introvert | synonym
217 | introvertito :: introvert | synonym
218 | 内向 :: introvert | synonym
219 | ```
220 |
221 | ## Accessing concept edges with a given relation direction
222 |
223 | You can also query the relations that have a specific concept as target or source. This is achieved with `concept.edges_out` and `concept.edges_in`, as follows:
224 |
225 | ```python
226 | from conceptnet_lite import Label
227 |
228 | concepts = Label.get(text='introvert', language='en').concepts
229 | for c in concepts:
230 | print(" Concept text:", c.text)
231 | if c.edges_out:
232 | print(" Edges out:")
233 | for e in c.edges_out:
234 | print(" Edge URI:", e.uri)
235 | print(" Relation:", e.relation.name)
236 | print(" End:", e.end.text)
237 | if c.edges_in:
238 | print(" Edges in:")
239 | for e in c.edges_in:
240 | print(" Edge URI:", e.uri)
241 | print(" Relation:", e.relation.name)
242 | print(" End:", e.end.text)
243 | ```
244 | ```console
245 | Concept text: introvert
246 | Edges out:
247 | Edge URI: /a/[/r/etymologically_derived_from/,/c/en/introvert/,/c/la/introvertere/]
248 | Relation: etymologically_derived_from
249 | End: introvertere
250 | ...
251 | Edges in:
252 | Edge URI: /a/[/r/antonym/,/c/cs/extrovert/n/,/c/en/introvert/]
253 | Relation: antonym
254 | End: introvert
255 | ...
256 | ```
257 |
258 | ## Traversing all the data for a language
259 |
260 | You can go over all concepts for a given language. For illustration, let us try Old Norse, a "small" language with the code "non" and vocab size of 7868, according to the [ConceptNet language statistics](https://github.com/commonsense/conceptnet5/wiki/Languages).
261 |
262 | ```python
263 | from conceptnet_lite import Language
264 |
265 | mylanguage = Language.get(name='non')
266 | for l in mylanguage.labels:
267 | print(" Label:", l.text)
268 | for c in l.concepts:
269 | print(" Concept URI:", c.uri)
270 | if c.edges_out:
271 | print(" Edges out:")
272 | for e in c.edges_out:
273 | print(" Edge URI:", e.uri)
274 | if c.edges_in:
275 | print(" Edges in:")
276 | for e in c.edges_in:
277 | print(" Edge URI:", e.uri)
278 | ```
279 | ```console
280 | Label: andsœlis
281 | Concept URI: /c/non/andsœlis/r
282 | Edges out:
283 | Edge URI: /a/[/r/antonym/,/c/non/andsœlis/r/,/c/non/réttsœlis/]
284 | Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/against/]
285 | Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/course/]
286 | Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/sun/]
287 | Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/widdershins/]
288 | Edge URI: /a/[/r/synonym/,/c/non/andsœlis/r/,/c/non/rangsœlis/]
289 | Concept URI: /c/non/andsœlis
290 | Edges out:
291 | Edge URI: /a/[/r/external_url/,/c/non/andsœlis/,/c/en.wiktionary.org/wiki/andsœlis/]
292 | Label: réttsœlis
293 | Concept URI: /c/non/réttsœlis
294 | Edges in:
295 | Edge URI: /a/[/r/antonym/,/c/non/andsœlis/r/,/c/non/réttsœlis/]
296 | ...
297 | ```
298 |
299 | ## Accessing Concepts by URI
300 |
301 | You can access concept ORM objects directly by providing a desired ConceptNet URI. This is done as follows:
302 |
303 | ```python
304 | from conceptnet_lite import Concept
305 |
306 | edge_object = Edge.get(start='/c/en/example')
307 | concept_object = Concept.get(uri='/c/en/example')
308 | ```
309 |
--------------------------------------------------------------------------------
/conceptnet_lite/__init__.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from pathlib import Path
3 | from typing import Iterable, Optional, Union
4 |
5 | import peewee
6 |
7 | from conceptnet_lite.db import CONCEPTNET_EDGE_COUNT, CONCEPTNET_DUMP_DOWNLOAD_URL, CONCEPTNET_DB_NAME
8 | from conceptnet_lite.db import CONCEPTNET_DB_URL
9 | from conceptnet_lite.db import Concept, Language, Label, Relation, RelationName, Edge
10 | from conceptnet_lite.db import prepare_db, _open_db, _generate_db_path, download_db
11 | from conceptnet_lite.utils import PathOrStr, _to_snake_case
12 |
13 |
14 | def connect(
15 | db_path: PathOrStr = CONCEPTNET_DB_NAME,
16 | db_download_url: Optional[str] = CONCEPTNET_DB_URL,
17 | delete_compressed_db: bool = True,
18 | dump_download_url: str = CONCEPTNET_DUMP_DOWNLOAD_URL,
19 | load_dump_edge_count: int = CONCEPTNET_EDGE_COUNT,
20 | delete_compressed_dump: bool = True,
21 | delete_dump: bool = True,
22 | ) -> None:
23 | """Connect to ConceptNet database.
24 |
25 | This function connects to ConceptNet database. If it does not exists, there are two options: to download ready
26 | database or to download the compressed ConceptNet dump, extract it, and load it
27 | into database (pass `db_download_url=None` for this option).
28 |
29 | Args:
30 | db_path: Path to the database.
31 | db_download_url: Link to compressed ConceptNet database. Pass `None` to build the db from dump.
32 | delete_compressed_db: Delete compressed database after extraction.
33 | dump_download_url: Link to compressed ConceptNet dump.
34 | load_dump_edge_count: Number of edges to load from the beginning of the dump file. Can be useful for testing.
35 | delete_compressed_dump: Delete compressed dump after unpacking.
36 | delete_dump: Delete dump after loading into database.
37 | """
38 | db_path = Path(db_path).expanduser().resolve()
39 | if db_path.is_dir():
40 | db_path = _generate_db_path(db_path)
41 | try:
42 | if db_path.is_file():
43 | _open_db(path=db_path)
44 | else:
45 | raise FileNotFoundError(2, "No such file", str(db_path))
46 | except FileNotFoundError:
47 | print(f"File not found: {db_path}")
48 | if db_download_url is not None:
49 | download_db(
50 | url=db_download_url,
51 | db_path=db_path,
52 | delete_compressed_db=delete_compressed_db,
53 | )
54 | _open_db(db_path)
55 | else:
56 | prepare_db(
57 | db_path=db_path,
58 | dump_download_url=dump_download_url,
59 | load_dump_edge_count=load_dump_edge_count,
60 | delete_compressed_dump=delete_compressed_dump,
61 | delete_dump=delete_dump,
62 | )
63 |
64 |
65 | def _get_where_clause_for_relation(relation: Optional[Union[Relation, str]] = None) -> Union[peewee.Expression, bool]:
66 | if relation is None:
67 | return True
68 | else:
69 | if isinstance(relation, str):
70 | relation = Relation.get(name=relation)
71 | return Edge.relation == relation
72 |
73 |
74 | class PartOfEdge(Enum):
75 | ANY = 'any'
76 | START = 'start'
77 | END = 'end'
78 |
79 |
80 | def edges(
81 | part_of_edge: PartOfEdge,
82 | concepts: Iterable[Concept],
83 | relation: Optional[Union[Relation, str]] = None,
84 | same_language: bool = False,
85 | ) -> peewee.ModelSelect:
86 | concepts = list(concepts)
87 | ConceptAlias = Concept.alias()
88 | if part_of_edge == PartOfEdge.ANY:
89 | in_concepts_where_clause = Edge.start.in_(concepts) | Edge.end.in_(concepts)
90 | concept_alias_join_on = (
91 | (Edge.end.in_(concepts) & (ConceptAlias.id == Edge.start)) |
92 | (Edge.start.in_(concepts) & (ConceptAlias.id == Edge.end)))
93 | elif part_of_edge == PartOfEdge.START:
94 | in_concepts_where_clause = Edge.start.in_(concepts)
95 | concept_alias_join_on = ConceptAlias.id == Edge.end
96 | elif part_of_edge == PartOfEdge.END:
97 | in_concepts_where_clause = Edge.end.in_(concepts)
98 | concept_alias_join_on = ConceptAlias.id == Edge.start
99 | else:
100 | assert False, "Unknown part_of_edge"
101 | result = (Edge.select()
102 | .where(in_concepts_where_clause & _get_where_clause_for_relation(relation)))
103 | if same_language:
104 | cte = result.cte('result')
105 | result = (Edge.select()
106 | .join(cte, on=(Edge.id == cte.c.id))
107 | .join(ConceptAlias, on=concept_alias_join_on)
108 | .join(Label)
109 | .join(Language)
110 | .where(Language.id == concepts[0].label.language)
111 | .with_cte(cte))
112 | return result
113 |
114 |
115 | def edges_from(
116 | start_concepts: Iterable[Concept],
117 | relation: Optional[Union[Relation, str]] = None,
118 | same_language: bool = False,
119 | ) -> peewee.ModelSelect:
120 | return edges(
121 | part_of_edge=PartOfEdge.START,
122 | concepts=start_concepts,
123 | relation=relation,
124 | same_language=same_language)
125 |
126 |
127 | def edges_to(
128 | end_concepts: Iterable[Concept],
129 | relation: Optional[Union[Relation, str]] = None,
130 | same_language: bool = False,
131 | ) -> peewee.ModelSelect:
132 | return edges(
133 | part_of_edge=PartOfEdge.END,
134 | concepts=end_concepts,
135 | relation=relation,
136 | same_language=same_language)
137 |
138 |
139 | def edges_for(
140 | concepts: Iterable[Concept],
141 | relation: Optional[Union[Relation, str]] = None,
142 | same_language: bool = False,
143 | ) -> peewee.ModelSelect:
144 | return edges(
145 | part_of_edge=PartOfEdge.ANY,
146 | concepts=concepts,
147 | relation=relation,
148 | same_language=same_language)
149 |
150 |
151 | def edges_between(
152 | start_concepts: Iterable[Concept],
153 | end_concepts: Iterable[Concept],
154 | relation: Optional[Union[Relation, str]] = None,
155 | two_way: bool = False,
156 | ) -> peewee.ModelSelect:
157 | condition = Edge.start.in_(start_concepts) & Edge.end.in_(end_concepts)
158 | if two_way:
159 | condition |= Edge.start.in_(end_concepts) & Edge.end.in_(start_concepts)
160 | condition &= _get_where_clause_for_relation(relation)
161 | return Edge.select().where(condition)
162 |
--------------------------------------------------------------------------------
/conceptnet_lite/db.py:
--------------------------------------------------------------------------------
1 | import csv
2 | import gzip
3 | import shutil
4 | import struct
5 | import zipfile
6 | from functools import partial
7 | from pathlib import Path
8 | from typing import Optional, Generator, Tuple
9 | from uuid import uuid4
10 |
11 | import lmdb
12 | from pySmartDL import SmartDL
13 | from tqdm import tqdm
14 | from peewee import DatabaseProxy, Model, TextField, ForeignKeyField
15 | from playhouse.sqlite_ext import JSONField, SqliteExtDatabase
16 |
17 | from conceptnet_lite.utils import PathOrStr, _to_snake_case
18 |
19 |
20 | class RelationName:
21 | """Names of non-deprecated relations.
22 |
23 | See: https://github.com/commonsense/conceptnet5/wiki/Relations.
24 | """
25 |
26 | RELATED_TO = 'related_to'
27 | FORM_OF = 'form_of'
28 | IS_A = 'is_a'
29 | PART_OF = 'part_of'
30 | HAS_A = 'has_a'
31 | USED_FOR = 'used_for'
32 | CAPABLE_OF = 'capable_of'
33 | AT_LOCATION = 'at_location'
34 | CAUSES = 'causes'
35 | HAS_SUBEVENT = 'has_subevent'
36 | HAS_FIRST_SUBEVENT = 'has_first_subevent'
37 | HAS_LAST_SUBEVENT = 'has_last_subevent'
38 | HAS_PREREQUISITE = 'has_prerequisite'
39 | HAS_PROPERTY = 'has_property'
40 | MOTIVATED_BY_GOAL = 'motivated_by_goal'
41 | OBSTRUCTED_BY = 'obstructed_by'
42 | DESIRES = 'desires'
43 | CREATED_BY = 'created_by'
44 | SYNONYM = 'synonym'
45 | ANTONYM = 'antonym'
46 | DISTINCT_FROM = 'distinct_from'
47 | DERIVED_FROM = 'derived_from'
48 | SYMBOL_OF = 'symbol_of'
49 | DEFINED_AS = 'defined_as'
50 | MANNER_OF = 'manner_of'
51 | LOCATED_NEAR = 'located_near'
52 | HAS_CONTEXT = 'has_context'
53 | SIMILAR_TO = 'similar_to'
54 | ETYMOLOGICALLY_RELATED_TO = 'etymologically_related_to'
55 | ETYMOLOGICALLY_DERIVED_FROM = 'etymologically_derived_from'
56 | CAUSES_DESIRE = 'causes_desire'
57 | MADE_OF = 'made_of'
58 | RECEIVES_ACTION = 'receives_action'
59 | EXTERNAL_URL = 'external_url'
60 |
61 |
62 | db = DatabaseProxy()
63 |
64 |
65 | class _BaseModel(Model):
66 | class Meta:
67 | database = db
68 |
69 |
70 | class Relation(_BaseModel):
71 | """Relation ORM class.
72 |
73 | See: https://github.com/commonsense/conceptnet5/wiki/Relations.
74 | """
75 |
76 | name = TextField(unique=True)
77 |
78 | def __str__(self):
79 | return self.uri
80 |
81 | @property
82 | def uri(self) -> str:
83 | return f'/r/{self.name}'
84 |
85 |
86 | class Language(_BaseModel):
87 | """Language ORM class.
88 |
89 | See: https://github.com/commonsense/conceptnet5/wiki/Languages.
90 | """
91 |
92 | name = TextField(unique=True)
93 |
94 | def __str__(self):
95 | return self.name
96 |
97 |
98 | class Label(_BaseModel):
99 | """Label ORM class.
100 |
101 | :class:`Label` can be seen as a part of :class:`Concept`. :class:`Label` is basically a text on a certain language
102 | (most often, a word).
103 |
104 | This abstraction is not present in the original ConceptNet. Class is introduced for the purposes of normalization.
105 | """
106 |
107 | text = TextField(index=True)
108 | language = ForeignKeyField(Language, backref='labels', null=True)
109 |
110 | def __str__(self):
111 | language_postfix = ' ({self.language.name})' if self.language is not None else ''
112 | return f'{self.text}{language_postfix}'
113 |
114 | @classmethod
115 | def get(cls, *query, **filters):
116 | if isinstance(filters.get('language'), str):
117 | filters['language'] = Language.get(name=filters['language'])
118 | return super().get(*query, **filters)
119 |
120 |
121 | class Concept(_BaseModel):
122 | """Concept ORM class.
123 |
124 | :class:`Concept` represents node in ConceptNet knowledge graph. It provides properties :attr:`language` and
125 | :attr:`text` that are aliases for corresponding :attr:`Label.language` and :attr:`Label.text` fields.
126 |
127 | This abstraction is not present in the original ConceptNet. Class is introduced for the purposes of normalization.
128 | """
129 |
130 | label = ForeignKeyField(Label, backref='concepts')
131 | sense_label = TextField()
132 |
133 | def __str__(self):
134 | return self.uri
135 |
136 | @classmethod
137 | def get(cls, *query, **filters):
138 | if 'uri' in filters:
139 | split_uri = filters['uri'].split('/', maxsplit=4)
140 | label = Label.get(text=split_uri[3], language=split_uri[2])
141 | sense_label = '' if len(split_uri) == 4 else split_uri[4]
142 | return Concept.get(label=label, sense_label=sense_label)
143 | return super().get(*query, **filters)
144 |
145 | @property
146 | def uri(self) -> str:
147 | if self.sense_label != 'url':
148 | ending = f'/{self.sense_label}' if self.sense_label else ''
149 | return f'/c/{self.language.name}/{self.text}{ending}'
150 | else:
151 | return f'{self.text}'
152 |
153 | @property
154 | def language(self) -> Language:
155 | return self.label.language
156 |
157 | @property
158 | def text(self) -> str:
159 | return self.label.text
160 |
161 |
162 | class Edge(_BaseModel):
163 | """Edge ORM class.
164 |
165 | See: https://github.com/commonsense/conceptnet5/wiki/Edges.
166 |
167 | Everything except relation, start, and end nodes is stored in :attr:`etc` field that is plain :class:`dict`.
168 | """
169 |
170 | relation = ForeignKeyField(Relation, backref='edges')
171 | start = ForeignKeyField(Concept, backref='edges_out')
172 | end = ForeignKeyField(Concept, backref='edges_in')
173 | etc = JSONField()
174 |
175 | def __str__(self):
176 | return self.uri
177 |
178 | @classmethod
179 | def get(cls, *query, **filters):
180 | if isinstance(filters.get('relation'), str):
181 | filters['relation'] = Relation.get(name=filters['relation'])
182 | if isinstance(filters.get('start'), str):
183 | filters['start'] = Concept.get(uri=filters['start'])
184 | if isinstance(filters.get('end'), str):
185 | filters['end'] = Concept.get(uri=filters['end'])
186 | return super().get(*query, **filters)
187 |
188 | @property
189 | def uri(self) -> str:
190 | return f'/a/[{self.relation.uri}/,{self.start.uri}/,{self.end.uri}/]'
191 |
192 |
193 | def _open_db(path: PathOrStr):
194 | db.initialize(SqliteExtDatabase(str(path), pragmas={
195 | 'synchronous': 0,
196 | 'cache_size': -1024 * 64,
197 | }))
198 | tables = [Relation, Language, Label, Concept, Edge]
199 | db.create_tables(tables)
200 |
201 |
202 | # For ConceptNet 5.7:
203 | CONCEPTNET_DUMP_DOWNLOAD_URL = (
204 | 'https://s3.amazonaws.com/conceptnet/downloads/2019/edges/conceptnet-assertions-5.7.0.csv.gz')
205 | CONCEPTNET_DB_URL = 'https://filedn.com/l4OYewCIMcOmtnyW43I0CuB/conceptnet.db.zip'
206 | CONCEPTNET_EDGE_COUNT = 34074917
207 | CONCEPTNET_DB_NAME = 'conceptnet.db'
208 |
209 |
210 | def _get_download_destination_path(dir_path: Path, url: str) -> Path:
211 | return dir_path / url.rsplit('/')[-1]
212 |
213 |
214 | def download_dump(
215 | url: str = CONCEPTNET_DUMP_DOWNLOAD_URL,
216 | out_dir_path: PathOrStr = Path.cwd(),
217 | ):
218 | """Download compressed ConceptNet dump.
219 |
220 | Args:
221 | url: Link to the dump.
222 | out_dir_path: Dir where to store dump.
223 | """
224 |
225 | print("Download compressed dump")
226 | compressed_dump_path = _get_download_destination_path(out_dir_path, url)
227 | if compressed_dump_path.is_file():
228 | raise FileExistsError(17, "File already exists", str(compressed_dump_path))
229 | downloader = SmartDL(url, str(compressed_dump_path))
230 | downloader.start()
231 |
232 |
233 | def extract_compressed_dump(
234 | compressed_dump_path: PathOrStr,
235 | delete_compressed_dump: bool = True,
236 | ):
237 | """Extract compressed ConceptNet dump.
238 |
239 | Args:
240 | compressed_dump_path: Path to compressed dump to extract.
241 | delete_compressed_dump: Delete compressed dump after extraction.
242 | """
243 |
244 | dump_path = Path(compressed_dump_path).with_suffix('')
245 | try:
246 | with gzip.open(str(compressed_dump_path), 'rb') as f_in:
247 | with open(str(dump_path), 'wb') as f_out:
248 | print("Extract compressed dump (this can take a few minutes)")
249 | shutil.copyfileobj(f_in, f_out)
250 | finally:
251 | if delete_compressed_dump and compressed_dump_path.is_file():
252 | compressed_dump_path.unlink()
253 |
254 |
255 | def load_dump_to_db(
256 | dump_path: PathOrStr,
257 | db_path: PathOrStr,
258 | edge_count: int = CONCEPTNET_EDGE_COUNT,
259 | delete_dump: bool = True,
260 | ):
261 | """Load dump to database.
262 |
263 | Args:
264 | dump_path: Path to dump to load.
265 | db_path: Path to resulting database.
266 | edge_count: Number of edges to load from the beginning of the dump file. Can be useful for testing.
267 | delete_dump: Delete dump after loading into database.
268 | """
269 |
270 | def edges_from_dump_by_parts_generator(
271 | count: Optional[int] = None,
272 | ) -> Generator[Tuple[str, str, str, str], None, None]:
273 | with open(str(dump_path), newline='') as f:
274 | reader = csv.reader(f, delimiter='\t')
275 | for i, row in enumerate(reader):
276 | if i == count:
277 | break
278 | yield row[1:5]
279 |
280 | def extract_relation_name(uri: str) -> str:
281 | return _to_snake_case(uri[3:])
282 |
283 | def get_struct_format(length: int) -> str:
284 | return f'{length}Q'
285 |
286 | def pack_ints(*ints) -> bytes:
287 | return struct.pack(get_struct_format(length=len(ints)), *ints)
288 |
289 | def unpack_ints(buffer: bytes) -> Tuple[int, ...]:
290 | return struct.unpack(get_struct_format(len(buffer) // 8), buffer)
291 |
292 | def language_and_label_in_bytes(concept_uri: str, is_external_url: bool) -> Tuple[bytes, bytes]:
293 | if not is_external_url:
294 | return tuple(x.encode('utf8') for x in concept_uri.split('/', maxsplit=4)[2:4])[:2]
295 | else:
296 | return b'', concept_uri.encode('utf8')
297 |
298 | def normalize() -> None:
299 | """Normalize dump before loading into database using lmdb."""
300 |
301 | def normalize_relation() -> None:
302 | nonlocal relation_i
303 |
304 | name = extract_relation_name(relation_uri)
305 | relation_b = name.encode('utf8')
306 | relation_exists = txn.get(relation_b, db=relation_db) is not None
307 | if not relation_exists:
308 | relation_i += 1
309 | relation_i_b = pack_ints(relation_i)
310 | txn.put(relation_b, relation_i_b, db=relation_db)
311 |
312 | def normalize_concept(uri: str, is_external_url: bool = False) -> None:
313 | nonlocal language_i, label_i, concept_i
314 |
315 | language_b, label_b = language_and_label_in_bytes(concept_uri=uri, is_external_url=is_external_url)
316 |
317 | if not is_external_url:
318 | language_id_b = txn.get(language_b, db=language_db)
319 | if language_id_b is None:
320 | language_i += 1
321 | language_id_b = pack_ints(language_i)
322 | txn.put(language_b, language_id_b, db=language_db)
323 |
324 | label_language_b = label_b + b'/' + language_b
325 | label_id_b = txn.get(label_language_b, db=label_db)
326 | if label_id_b is None:
327 | label_i += 1
328 | label_id_b = pack_ints(label_i)
329 | txn.put(label_language_b, label_id_b, db=label_db)
330 |
331 | concept_b = uri.encode('utf8')
332 | concept_id_b = txn.get(concept_b, db=concept_db)
333 | if concept_id_b is None:
334 | concept_i += 1
335 | concept_id_b = pack_ints(concept_i)
336 | txn.put(concept_b, concept_id_b, db=concept_db)
337 |
338 | language_i, relation_i, label_i, concept_i = 4 * [0]
339 | if not dump_path.is_file():
340 | raise FileNotFoundError(2, 'No such file', str(dump_path))
341 | print('Dump normalization')
342 | edges = enumerate(edges_from_dump_by_parts_generator(count=edge_count))
343 | for i, (relation_uri, start_uri, end_uri, _) in tqdm(edges, unit=' edges', total=edge_count):
344 | normalize_relation()
345 | normalize_concept(start_uri)
346 | is_end_uri_external_url = extract_relation_name(relation_uri) == RelationName.EXTERNAL_URL
347 | normalize_concept(end_uri, is_external_url=is_end_uri_external_url)
348 |
349 | def insert() -> None:
350 | """Load dump from CSV and lmdb database into database."""
351 |
352 | def insert_objects_from_edge():
353 | nonlocal edge_i
354 |
355 | def insert_relation() -> Tuple[int, bool]:
356 | nonlocal relation_i
357 |
358 | name = extract_relation_name(relation_uri)
359 | relation_b = name.encode('utf8')
360 | result_id, = unpack_ints(buffer=txn.get(relation_b, db=relation_db))
361 | if result_id == relation_i:
362 | db.execute_sql('insert into relation (name) values (?)', (name, ))
363 | relation_i += 1
364 | return result_id, name == RelationName.EXTERNAL_URL
365 |
366 | def insert_concept(uri: str, is_external_url: bool = False) -> int:
367 | nonlocal language_i, label_i, concept_i
368 |
369 | split_uri = uri.split('/', maxsplit=4)
370 |
371 | language_b, label_b = language_and_label_in_bytes(concept_uri=uri, is_external_url=is_external_url)
372 |
373 | if not is_external_url:
374 | language_id, = unpack_ints(buffer=txn.get(language_b, db=language_db))
375 | if language_id == language_i:
376 | name = split_uri[2]
377 | db.execute_sql('insert into language (name) values (?)', (name, ))
378 | language_i += 1
379 | else:
380 | language_id = None
381 |
382 | label_language_b = label_b + b'/' + language_b
383 | label_id, = unpack_ints(buffer=txn.get(label_language_b, db=label_db))
384 | if label_id == label_i:
385 | text = split_uri[3] if not is_external_url else uri
386 | params = (text, language_id)
387 | db.execute_sql('insert into label (text, language_id) values (?, ?)', params)
388 | label_i += 1
389 |
390 | concept_b = uri.encode('utf8')
391 | concept_id, = unpack_ints(buffer=txn.get(concept_b, db=concept_db))
392 | if concept_id == concept_i:
393 | sense_label = ('' if len(split_uri) == 4 else split_uri[4]) if not is_external_url else 'url'
394 | params = (label_id, sense_label)
395 | db.execute_sql('insert into concept (label_id, sense_label) values (?, ?)', params)
396 | concept_i += 1
397 | return concept_id
398 |
399 | def insert_edge() -> None:
400 | params = (relation_id, start_id, end_id, edge_etc)
401 | db.execute_sql('insert into edge (relation_id, start_id, end_id, etc) values (?, ?, ?, ?)', params)
402 |
403 | relation_id, is_external_url = insert_relation()
404 | start_id = insert_concept(uri=start_uri)
405 | end_id = insert_concept(uri=end_uri, is_external_url=is_external_url)
406 | insert_edge()
407 | edge_i += 1
408 |
409 | print('Dump insertion')
410 | relation_i, language_i, label_i, concept_i, edge_i = 5 * [1]
411 | edges = edges_from_dump_by_parts_generator(count=edge_count)
412 | progress_bar = tqdm(unit=' edges', total=edge_count)
413 | finished = False
414 | while not finished:
415 | edge_count_per_insert = 1000000
416 | with db.atomic():
417 | for _ in range(edge_count_per_insert):
418 | try:
419 | relation_uri, start_uri, end_uri, edge_etc = next(edges)
420 | except StopIteration:
421 | finished = True
422 | break
423 | insert_objects_from_edge()
424 | progress_bar.update()
425 |
426 | GIB = 1 << 30
427 | dump_path = Path(dump_path)
428 | lmdb_db_path = dump_path.parent / f'conceptnet-lmdb-{uuid4()}.db'
429 | env = lmdb.open(str(lmdb_db_path), map_size=4*GIB, max_dbs=5, sync=False, writemap=False)
430 | relation_db = env.open_db(b'relation')
431 | language_db = env.open_db(b'language')
432 | label_db = env.open_db(b'label')
433 | concept_db = env.open_db(b'concept')
434 | try:
435 | with env.begin(write=True) as txn:
436 | normalize()
437 | _open_db(path=db_path)
438 | insert()
439 | finally:
440 | shutil.rmtree(str(lmdb_db_path), ignore_errors=True)
441 | if delete_dump and dump_path.is_file():
442 | dump_path.unlink()
443 |
444 |
445 | def _generate_db_path(db_dir_path: Path) -> Path:
446 | return db_dir_path / CONCEPTNET_DB_NAME
447 |
448 |
449 | def prepare_db(
450 | db_path: PathOrStr,
451 | dump_download_url: str = CONCEPTNET_DUMP_DOWNLOAD_URL,
452 | load_dump_edge_count: int = CONCEPTNET_EDGE_COUNT,
453 | delete_compressed_dump: bool = True,
454 | delete_dump: bool = True,
455 | ):
456 | """Prepare ConceptNet database.
457 |
458 | This function downloads the compressed ConceptNet dump, extracts it, and loads it into database. First two steps
459 | are optional, and are executed only if needed.
460 |
461 | Args:
462 | db_path: Path to the resulting database.
463 | dump_download_url: Link to compressed ConceptNet dump.
464 | load_dump_edge_count: Number of edges to load from the beginning of the dump file. Can be useful for testing.
465 | delete_compressed_dump: Delete compressed dump after extraction.
466 | delete_dump: Delete dump after loading into database.
467 | """
468 |
469 | db_path = Path(db_path).expanduser().resolve()
470 | if db_path.is_dir():
471 | db_path = _generate_db_path(db_path)
472 | if db_path.is_file():
473 | raise FileExistsError(17, "File already exists and it is not a valid database", str(db_path))
474 |
475 | print("Prepare database")
476 | compressed_dump_path = _get_download_destination_path(db_path.parent, CONCEPTNET_DUMP_DOWNLOAD_URL)
477 | dump_path = compressed_dump_path.with_suffix('')
478 |
479 | db_path.parent.mkdir(parents=True, exist_ok=True)
480 |
481 | load_dump_to_db_ = partial(
482 | load_dump_to_db,
483 | dump_path=dump_path,
484 | db_path=db_path,
485 | edge_count=load_dump_edge_count,
486 | delete_dump=delete_dump,
487 | )
488 | extract_compressed_dump_ = partial(
489 | extract_compressed_dump,
490 | compressed_dump_path=compressed_dump_path,
491 | delete_compressed_dump=delete_compressed_dump,
492 | )
493 | download_dump_ = partial(
494 | download_dump,
495 | url=dump_download_url,
496 | out_dir_path=db_path.parent,
497 | )
498 |
499 | try:
500 | load_dump_to_db_()
501 | except FileNotFoundError:
502 | try:
503 | extract_compressed_dump_()
504 | load_dump_to_db_()
505 | except FileNotFoundError:
506 | download_dump_()
507 | extract_compressed_dump_()
508 | load_dump_to_db_()
509 | finally:
510 | if delete_compressed_dump and compressed_dump_path.is_file():
511 | compressed_dump_path.unlink()
512 | if delete_dump and dump_path.is_file():
513 | dump_path.unlink()
514 |
515 |
516 | def download_db(
517 | url: str = CONCEPTNET_DB_URL,
518 | db_path: PathOrStr = CONCEPTNET_DB_NAME,
519 | delete_compressed_db: bool = True
520 | ) -> None:
521 | """Download compressed ConceptNet dump and extract it.
522 |
523 | Args:
524 | url: Link to compressed ConceptNet database.
525 | db_path: Path to resulting database.
526 | delete_compressed_db: Delete compressed database after extraction.
527 | """
528 |
529 | print("Download compressed database")
530 | db_path = Path(db_path).expanduser().resolve()
531 | if db_path.is_dir():
532 | db_path = _generate_db_path(db_path)
533 | if db_path.is_file():
534 | raise FileExistsError(17, "File already exists", str(db_path))
535 | compressed_db_path = _get_download_destination_path(db_path.parent, url)
536 | if compressed_db_path.is_file():
537 | raise FileExistsError(17, "File already exists", str(compressed_db_path))
538 | downloader = SmartDL(url, str(compressed_db_path))
539 | downloader.start()
540 | try:
541 | with zipfile.ZipFile(str(compressed_db_path), 'r') as zip_f:
542 | print("Extract compressed database (this can take a few minutes)")
543 | zip_f.extractall(db_path.parent)
544 | if db_path.name != CONCEPTNET_DB_NAME:
545 | Path(db_path.parent / CONCEPTNET_DB_NAME).rename(db_path)
546 | finally:
547 | if delete_compressed_db and compressed_db_path.is_file():
548 | compressed_db_path.unlink()
549 |
--------------------------------------------------------------------------------
/conceptnet_lite/utils.py:
--------------------------------------------------------------------------------
1 | import re
2 | from pathlib import Path
3 | from typing import Union
4 |
5 |
6 | PathOrStr = Union[Path, str]
7 |
8 |
9 | def _to_snake_case(s: str) -> str:
10 | regex = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
11 | return regex.sub(r'_\1', s).lower()
12 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | lmdb==1.4.1
2 | peewee==3.10.0
3 | pySmartDL==1.3.2
4 | sphinx-autodoc-typehints==1.7.0
5 | tqdm==4.66.3
6 |
--------------------------------------------------------------------------------
/docs/source/_static/erd.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
174 |
--------------------------------------------------------------------------------
/docs/source/conceptnet_lite.rst:
--------------------------------------------------------------------------------
1 | conceptnet\_lite
2 | ========================
3 |
4 |
5 | Module contents
6 | ---------------
7 |
8 | .. automodule:: conceptnet_lite
9 | :members:
10 | :undoc-members:
11 | :show-inheritance:
12 |
13 |
14 | conceptnet\_lite.db module
15 | --------------------------
16 |
17 | .. automodule:: conceptnet_lite.db
18 | :members:
19 | :undoc-members:
20 | :show-inheritance:
21 |
22 | database schema
23 | ---------------
24 |
25 | .. image:: _static/erd.svg
26 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # http://www.sphinx-doc.org/en/master/config
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | # import os
14 | # import sys
15 | # sys.path.insert(0, os.path.abspath('.'))
16 | import os
17 | import sys
18 | sys.path.insert(0, os.path.abspath('../..'))
19 |
20 |
21 | # -- Project information -----------------------------------------------------
22 |
23 | project = 'conceptnet-lite'
24 | copyright = '2019, LDT team'
25 | author = 'LDT team'
26 |
27 | # The full version, including alpha/beta/rc tags
28 | release = '0.1.27'
29 |
30 |
31 | # -- General configuration ---------------------------------------------------
32 |
33 | # Add any Sphinx extension module names here, as strings. They can be
34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
35 | # ones.
36 | extensions = [
37 | 'sphinx.ext.autodoc',
38 | 'sphinx.ext.napoleon',
39 | 'sphinx_autodoc_typehints',
40 | ]
41 |
42 | # Add any paths that contain templates here, relative to this directory.
43 | templates_path = ['_templates']
44 |
45 | # List of patterns, relative to source directory, that match files and
46 | # directories to ignore when looking for source files.
47 | # This pattern also affects html_static_path and html_extra_path.
48 | exclude_patterns = []
49 |
50 |
51 | # -- Options for HTML output -------------------------------------------------
52 |
53 | # The theme to use for HTML and HTML Help pages. See the documentation for
54 | # a list of builtin themes.
55 | #
56 | html_theme = 'sphinx_rtd_theme'
57 |
58 | # Add any paths that contain custom static files (such as style sheets) here,
59 | # relative to this directory. They are copied after the builtin static files,
60 | # so a file named "default.css" will overwrite the builtin "default.css".
61 | html_static_path = ['_static']
62 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. conceptnet-lite documentation master file, created by
2 | sphinx-quickstart on Sat Aug 31 19:37:58 2019.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to conceptnet-lite's documentation!
7 | ===========================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | conceptnet_lite
14 |
15 |
16 | Indices and tables
17 | ==================
18 |
19 | * :ref:`genindex`
20 | * :ref:`modindex`
21 | * :ref:`search`
22 |
--------------------------------------------------------------------------------
/examples/conceptnet_queries.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import conceptnet_lite
4 | from conceptnet_lite import Label, Language, edges_for, edges_between
5 |
6 |
7 | base_dir_path = Path('~/conceptnet-lite-data')
8 | conceptnet_lite.connect(
9 | db_path=base_dir_path / 'conceptnet.db',
10 | dump_dir_path=base_dir_path,
11 | )
12 |
13 | russian = Language.get(name='ru')
14 |
15 | print("All edges between 'рай' и 'ад':")
16 | heaven_concepts = Label.get(text='рай', language=russian).concepts
17 | hell_concepts = Label.get(text='ад', language=russian).concepts
18 | for e in edges_between(heaven_concepts, hell_concepts, two_way=True):
19 | print(" Edge URI:", e.uri)
20 | print(" Relation:", e.relation.name)
21 |
22 | english = Language.get(name='en')
23 |
24 | print("Get edges for 'introvert':")
25 | introvert_concepts = Label.get(text='introvert', language=english).concepts
26 | for e in edges_for(introvert_concepts, same_language=True):
27 | print(" Edge URI:", e.uri)
28 |
29 | print("Traversing Russian:")
30 | for l in russian.labels:
31 | print(" Label:", l.text)
32 | for c in l.concepts:
33 | print(" Concept URI:", c.uri)
34 | print(" Concept language:", c.language.name)
35 | if c.edges_out:
36 | print(" Edges out:")
37 | for e in c.edges_out:
38 | print(" Edge URI:", e.uri)
39 | print(" Relation:", e.relation.name)
40 | print(" End:", e.end.uri)
41 | if c.edges_in:
42 | print(" Edges in:")
43 | for e in c.edges_in:
44 | print(" Edge URI:", e.uri)
45 | print(" Relation:", e.relation.name)
46 | print(" End:", e.end.uri)
47 |
--------------------------------------------------------------------------------
/examples/download_and_load_dump_to_db.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import conceptnet_lite
4 |
5 |
6 | base_dir_path = Path('~/conceptnet-lite-data')
7 | conceptnet_lite.connect(
8 | db_path=base_dir_path / 'conceptnet.db',
9 | dump_dir_path=base_dir_path,
10 | delete_dump=False,
11 | delete_compressed_dump=False,
12 | )
13 |
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
2 |
3 | [[package]]
4 | name = "alabaster"
5 | version = "0.7.13"
6 | description = "A configurable sidebar-enabled Sphinx theme"
7 | optional = false
8 | python-versions = ">=3.6"
9 | files = [
10 | {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
11 | {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
12 | ]
13 |
14 | [[package]]
15 | name = "babel"
16 | version = "2.14.0"
17 | description = "Internationalization utilities"
18 | optional = false
19 | python-versions = ">=3.7"
20 | files = [
21 | {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
22 | {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
23 | ]
24 |
25 | [package.dependencies]
26 | pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
27 |
28 | [package.extras]
29 | dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
30 |
31 | [[package]]
32 | name = "certifi"
33 | version = "2024.7.4"
34 | description = "Python package for providing Mozilla's CA Bundle."
35 | optional = false
36 | python-versions = ">=3.6"
37 | files = [
38 | {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
39 | {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
40 | ]
41 |
42 | [[package]]
43 | name = "charset-normalizer"
44 | version = "3.3.2"
45 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
46 | optional = false
47 | python-versions = ">=3.7.0"
48 | files = [
49 | {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
50 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
51 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
52 | {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
53 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
54 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
55 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
56 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
57 | {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
58 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
59 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
60 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
61 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
62 | {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
63 | {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
64 | {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
65 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
66 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
67 | {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
68 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
69 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
70 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
71 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
72 | {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
73 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
74 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
75 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
76 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
77 | {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
78 | {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
79 | {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
80 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
81 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
82 | {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
83 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
84 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
85 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
86 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
87 | {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
88 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
89 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
90 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
91 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
92 | {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
93 | {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
94 | {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
95 | {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
96 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
97 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
98 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
99 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
100 | {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
101 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
102 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
103 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
104 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
105 | {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
106 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
107 | {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
108 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
109 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
110 | {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
111 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
112 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
113 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
114 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
115 | {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
116 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
117 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
118 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
119 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
120 | {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
121 | {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
122 | {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
123 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
124 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
125 | {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
126 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
127 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
128 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
129 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
130 | {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
131 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
132 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
133 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
134 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
135 | {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
136 | {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
137 | {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
138 | {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
139 | ]
140 |
141 | [[package]]
142 | name = "colorama"
143 | version = "0.4.6"
144 | description = "Cross-platform colored terminal text."
145 | optional = false
146 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
147 | files = [
148 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
149 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
150 | ]
151 |
152 | [[package]]
153 | name = "docutils"
154 | version = "0.17.1"
155 | description = "Docutils -- Python Documentation Utilities"
156 | optional = false
157 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
158 | files = [
159 | {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
160 | {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
161 | ]
162 |
163 | [[package]]
164 | name = "idna"
165 | version = "3.7"
166 | description = "Internationalized Domain Names in Applications (IDNA)"
167 | optional = false
168 | python-versions = ">=3.5"
169 | files = [
170 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
171 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
172 | ]
173 |
174 | [[package]]
175 | name = "imagesize"
176 | version = "1.4.1"
177 | description = "Getting image size from png/jpeg/jpeg2000/gif file"
178 | optional = false
179 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
180 | files = [
181 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"},
182 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
183 | ]
184 |
185 | [[package]]
186 | name = "jinja2"
187 | version = "3.1.4"
188 | description = "A very fast and expressive template engine."
189 | optional = false
190 | python-versions = ">=3.7"
191 | files = [
192 | {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
193 | {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
194 | ]
195 |
196 | [package.dependencies]
197 | MarkupSafe = ">=2.0"
198 |
199 | [package.extras]
200 | i18n = ["Babel (>=2.7)"]
201 |
202 | [[package]]
203 | name = "lmdb"
204 | version = "1.4.1"
205 | description = "Universal Python binding for the LMDB 'Lightning' Database"
206 | optional = false
207 | python-versions = "*"
208 | files = [
209 | {file = "lmdb-1.4.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:dcdbe27f75da9b8f58815c6ac9a1f8fa2d7a8d42abc22abb664e089002d5ffa4"},
210 | {file = "lmdb-1.4.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:032ce6f490caedbec642fc0a79114475e8520d1bf1e1465c6a12b8e5fe39022f"},
211 | {file = "lmdb-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13c5c8504d419039d6617cee24941e420d648a5b15c4b21e6491821400e5750f"},
212 | {file = "lmdb-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8018a947608c4be0dc885c90f477a600be1b71285059a9c68280d36b3fb29b"},
213 | {file = "lmdb-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:360ac42a8772f571fdd01156e0466d6be52eea1140556a138281b7c887916ae2"},
214 | {file = "lmdb-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f683f3d9a1771f21a7788a9be98fae9f3ce13cb8d549d6074d0402f284572458"},
215 | {file = "lmdb-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73332a830c72d76d57744cd2b29eca2c258bc406273ca4ee07dc9e48ae84d712"},
216 | {file = "lmdb-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c178c5134e942256a830b0bca7bb052d3d7c645b4b8759d720ab49ec36b3aae"},
217 | {file = "lmdb-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:12047c239ab6ccbbc9db99277aabcfe1c15b1cfc9ea33b92ab30ddd6f0823a10"},
218 | {file = "lmdb-1.4.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:91930a2a7eb9acc4d687f9067d6f9ec83c9673bbee55823badbbee2f9a3e9970"},
219 | {file = "lmdb-1.4.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:1b106eb7a23b6a224bc7dfe2bd5a34c84973dda039965ae99106e10d22833dd9"},
220 | {file = "lmdb-1.4.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d7779ccfacd5f4c62f28485dd2427b54d19dd7016000e6237816a3750287a82"},
221 | {file = "lmdb-1.4.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c1f1eff7ae8d8d534309f05e274fd646dd1d4abf5157c59db59a54a55463371"},
222 | {file = "lmdb-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b6354df94d241e8c0158f716902224109a5f3f7ed9a24447a25f968427f61d77"},
223 | {file = "lmdb-1.4.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:64cf7470edfc45ff0369956e40a0784b5225097569299b91f893bd50fa336f52"},
224 | {file = "lmdb-1.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3c15d344731507fcfddb911a86d325e867c5574751af28591e82ecf21aad1e5"},
225 | {file = "lmdb-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342550b86bb6275bfb89dbde9e48385da51d57124433bd464cd7681d0702f566"},
226 | {file = "lmdb-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3a99a3859427fbc273ae1e932b3e7da946089757e74a05a24a19f5c4a1aba933"},
227 | {file = "lmdb-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f71da9bd33fd17c9cdbe2bd4ce87f4b36b8f044927df4220bec4b03f209c78a2"},
228 | {file = "lmdb-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e9ff50ad20d890bc63524230237a61b6eb3be96ad6a6ac475e8ba1a1f2c751f"},
229 | {file = "lmdb-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81abf9475a62b7ced1ac0352967106b7ed1ac5d1c1a0d23ed24abe55a28f9884"},
230 | {file = "lmdb-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:c9fa31743b447a3fbbbdaefc858de1c761568d855155dec54d5ad490f88856b6"},
231 | {file = "lmdb-1.4.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:26ef8fa7bd34a64f78f5e16fa9bcce0fe2ad682dd26ef078f95a8847dacb1171"},
232 | {file = "lmdb-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f5dc8a335f7925fd667d62a5e43bed3aa35959b32b233fe0112a6ef02e07877"},
233 | {file = "lmdb-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ba5d78b0ff130b38a56b7161ceb7e27ba4364d827d2bbb251c24b06c28c64cd"},
234 | {file = "lmdb-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3b84f6a349ed1bd3fa4e6c3c6b711d0389cc8d9206733cb92feffaf102998e0c"},
235 | {file = "lmdb-1.4.1-pp27-pypy_73-macosx_10_7_x86_64.whl", hash = "sha256:a428e6b0e298290b91b7d0ce409f595c2c9027d7f2076c39ba006290b90d14cc"},
236 | {file = "lmdb-1.4.1-pp27-pypy_73-win_amd64.whl", hash = "sha256:885d3f3bf51b9167d368e37b1f1277eabf595dceefd69a489bd81c1ffd3d8ffd"},
237 | {file = "lmdb-1.4.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4bd8e49d5209c652b2caa18a3a4c30524025d7868d34b7bb249c42f7997da240"},
238 | {file = "lmdb-1.4.1.tar.gz", hash = "sha256:1f4c76af24e907593487c904ef5eba1993beb38ed385af82adb25a858f2d658d"},
239 | ]
240 |
241 | [[package]]
242 | name = "markupsafe"
243 | version = "2.1.3"
244 | description = "Safely add untrusted strings to HTML/XML markup."
245 | optional = false
246 | python-versions = ">=3.7"
247 | files = [
248 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
249 | {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
250 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
251 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
252 | {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
253 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
254 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
255 | {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
256 | {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
257 | {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
258 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
259 | {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
260 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
261 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
262 | {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
263 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
264 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
265 | {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
266 | {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
267 | {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
268 | {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
269 | {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
270 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
271 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
272 | {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
273 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
274 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
275 | {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
276 | {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
277 | {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
278 | {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
279 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
280 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
281 | {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
282 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
283 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
284 | {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
285 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
286 | {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
287 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
288 | {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
289 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
290 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
291 | {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
292 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
293 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
294 | {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
295 | {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
296 | {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
297 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
298 | {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
299 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
300 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
301 | {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
302 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
303 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
304 | {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
305 | {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
306 | {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
307 | {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
308 | ]
309 |
310 | [[package]]
311 | name = "packaging"
312 | version = "23.2"
313 | description = "Core utilities for Python packages"
314 | optional = false
315 | python-versions = ">=3.7"
316 | files = [
317 | {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
318 | {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
319 | ]
320 |
321 | [[package]]
322 | name = "peewee"
323 | version = "3.17.0"
324 | description = "a little orm"
325 | optional = false
326 | python-versions = "*"
327 | files = [
328 | {file = "peewee-3.17.0.tar.gz", hash = "sha256:3a56967f28a43ca7a4287f4803752aeeb1a57a08dee2e839b99868181dfb5df8"},
329 | ]
330 |
331 | [[package]]
332 | name = "pygments"
333 | version = "2.17.2"
334 | description = "Pygments is a syntax highlighting package written in Python."
335 | optional = false
336 | python-versions = ">=3.7"
337 | files = [
338 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
339 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
340 | ]
341 |
342 | [package.extras]
343 | plugins = ["importlib-metadata"]
344 | windows-terminal = ["colorama (>=0.4.6)"]
345 |
346 | [[package]]
347 | name = "pysmartdl"
348 | version = "1.3.4"
349 | description = "A Smart Download Manager for Python"
350 | optional = false
351 | python-versions = "*"
352 | files = [
353 | {file = "pySmartDL-1.3.4-py3-none-any.whl", hash = "sha256:671c277ca710fb9b6603b19176f5c091041ec4ef6dcdb507c9a983a89ca35d31"},
354 | {file = "pySmartDL-1.3.4.tar.gz", hash = "sha256:35275d1694f3474d33bdca93b27d3608265ffd42f5aeb28e56f38b906c0c35f4"},
355 | ]
356 |
357 | [[package]]
358 | name = "pytz"
359 | version = "2023.3.post1"
360 | description = "World timezone definitions, modern and historical"
361 | optional = false
362 | python-versions = "*"
363 | files = [
364 | {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
365 | {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
366 | ]
367 |
368 | [[package]]
369 | name = "requests"
370 | version = "2.32.0"
371 | description = "Python HTTP for Humans."
372 | optional = false
373 | python-versions = ">=3.8"
374 | files = [
375 | {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"},
376 | {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"},
377 | ]
378 |
379 | [package.dependencies]
380 | certifi = ">=2017.4.17"
381 | charset-normalizer = ">=2,<4"
382 | idna = ">=2.5,<4"
383 | urllib3 = ">=1.21.1,<3"
384 |
385 | [package.extras]
386 | socks = ["PySocks (>=1.5.6,!=1.5.7)"]
387 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
388 |
389 | [[package]]
390 | name = "setuptools"
391 | version = "70.0.0"
392 | description = "Easily download, build, install, upgrade, and uninstall Python packages"
393 | optional = false
394 | python-versions = ">=3.8"
395 | files = [
396 | {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
397 | {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
398 | ]
399 |
400 | [package.extras]
401 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
402 | testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
403 |
404 | [[package]]
405 | name = "snowballstemmer"
406 | version = "2.2.0"
407 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
408 | optional = false
409 | python-versions = "*"
410 | files = [
411 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
412 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
413 | ]
414 |
415 | [[package]]
416 | name = "sphinx"
417 | version = "2.4.5"
418 | description = "Python documentation generator"
419 | optional = false
420 | python-versions = ">=3.5"
421 | files = [
422 | {file = "Sphinx-2.4.5-py3-none-any.whl", hash = "sha256:02d7e9dc5f30caa42a682b26de408b755a55c7b07f356a30a3b6300bf7d4740e"},
423 | {file = "Sphinx-2.4.5.tar.gz", hash = "sha256:b00394e90463e7482c4cf59e7db1c8604baeca1468abfc062904dedc1cea6fcc"},
424 | ]
425 |
426 | [package.dependencies]
427 | alabaster = ">=0.7,<0.8"
428 | babel = ">=1.3,<2.0 || >2.0"
429 | colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
430 | docutils = ">=0.12,<0.18"
431 | imagesize = "*"
432 | Jinja2 = ">=2.3"
433 | packaging = "*"
434 | Pygments = ">=2.0"
435 | requests = ">=2.5.0"
436 | setuptools = "*"
437 | snowballstemmer = ">=1.1"
438 | sphinxcontrib-applehelp = "*"
439 | sphinxcontrib-devhelp = "*"
440 | sphinxcontrib-htmlhelp = "*"
441 | sphinxcontrib-jsmath = "*"
442 | sphinxcontrib-qthelp = "*"
443 | sphinxcontrib-serializinghtml = "*"
444 |
445 | [package.extras]
446 | docs = ["sphinxcontrib-websupport"]
447 | test = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-import-order", "html5lib", "mypy (>=0.761)", "pytest (<5.3.3)", "pytest-cov"]
448 |
449 | [[package]]
450 | name = "sphinx-autodoc-typehints"
451 | version = "1.10.3"
452 | description = "Type hints (PEP 484) support for the Sphinx autodoc extension"
453 | optional = false
454 | python-versions = ">=3.5.2"
455 | files = [
456 | {file = "sphinx-autodoc-typehints-1.10.3.tar.gz", hash = "sha256:a6b3180167479aca2c4d1ed3b5cb044a70a76cccd6b38662d39288ebd9f0dff0"},
457 | {file = "sphinx_autodoc_typehints-1.10.3-py3-none-any.whl", hash = "sha256:27c9e6ef4f4451766ab8d08b2d8520933b97beb21c913f3df9ab2e59b56e6c6c"},
458 | ]
459 |
460 | [package.dependencies]
461 | Sphinx = ">=2.1"
462 |
463 | [package.extras]
464 | test = ["dataclasses", "pytest (>=3.1.0)", "sphobjinv (>=2.0)", "typing-extensions (>=3.5)"]
465 | type-comments = ["typed-ast (>=1.4.0)"]
466 |
467 | [[package]]
468 | name = "sphinx-rtd-theme"
469 | version = "0.4.3"
470 | description = "Read the Docs theme for Sphinx"
471 | optional = false
472 | python-versions = "*"
473 | files = [
474 | {file = "sphinx_rtd_theme-0.4.3-py2.py3-none-any.whl", hash = "sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4"},
475 | {file = "sphinx_rtd_theme-0.4.3.tar.gz", hash = "sha256:728607e34d60456d736cc7991fd236afb828b21b82f956c5ea75f94c8414040a"},
476 | ]
477 |
478 | [package.dependencies]
479 | sphinx = "*"
480 |
481 | [[package]]
482 | name = "sphinxcontrib-applehelp"
483 | version = "1.0.4"
484 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
485 | optional = false
486 | python-versions = ">=3.8"
487 | files = [
488 | {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"},
489 | {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"},
490 | ]
491 |
492 | [package.extras]
493 | lint = ["docutils-stubs", "flake8", "mypy"]
494 | test = ["pytest"]
495 |
496 | [[package]]
497 | name = "sphinxcontrib-devhelp"
498 | version = "1.0.2"
499 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
500 | optional = false
501 | python-versions = ">=3.5"
502 | files = [
503 | {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
504 | {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
505 | ]
506 |
507 | [package.extras]
508 | lint = ["docutils-stubs", "flake8", "mypy"]
509 | test = ["pytest"]
510 |
511 | [[package]]
512 | name = "sphinxcontrib-htmlhelp"
513 | version = "2.0.1"
514 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
515 | optional = false
516 | python-versions = ">=3.8"
517 | files = [
518 | {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"},
519 | {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"},
520 | ]
521 |
522 | [package.extras]
523 | lint = ["docutils-stubs", "flake8", "mypy"]
524 | test = ["html5lib", "pytest"]
525 |
526 | [[package]]
527 | name = "sphinxcontrib-jsmath"
528 | version = "1.0.1"
529 | description = "A sphinx extension which renders display math in HTML via JavaScript"
530 | optional = false
531 | python-versions = ">=3.5"
532 | files = [
533 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
534 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
535 | ]
536 |
537 | [package.extras]
538 | test = ["flake8", "mypy", "pytest"]
539 |
540 | [[package]]
541 | name = "sphinxcontrib-qthelp"
542 | version = "1.0.3"
543 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
544 | optional = false
545 | python-versions = ">=3.5"
546 | files = [
547 | {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
548 | {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
549 | ]
550 |
551 | [package.extras]
552 | lint = ["docutils-stubs", "flake8", "mypy"]
553 | test = ["pytest"]
554 |
555 | [[package]]
556 | name = "sphinxcontrib-serializinghtml"
557 | version = "1.1.5"
558 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
559 | optional = false
560 | python-versions = ">=3.5"
561 | files = [
562 | {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"},
563 | {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
564 | ]
565 |
566 | [package.extras]
567 | lint = ["docutils-stubs", "flake8", "mypy"]
568 | test = ["pytest"]
569 |
570 | [[package]]
571 | name = "tqdm"
572 | version = "4.66.1"
573 | description = "Fast, Extensible Progress Meter"
574 | optional = false
575 | python-versions = ">=3.7"
576 | files = [
577 | {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"},
578 | {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"},
579 | ]
580 |
581 | [package.dependencies]
582 | colorama = {version = "*", markers = "platform_system == \"Windows\""}
583 |
584 | [package.extras]
585 | dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"]
586 | notebook = ["ipywidgets (>=6)"]
587 | slack = ["slack-sdk"]
588 | telegram = ["requests"]
589 |
590 | [[package]]
591 | name = "urllib3"
592 | version = "2.2.2"
593 | description = "HTTP library with thread-safe connection pooling, file post, and more."
594 | optional = false
595 | python-versions = ">=3.8"
596 | files = [
597 | {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
598 | {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
599 | ]
600 |
601 | [package.extras]
602 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
603 | h2 = ["h2 (>=4,<5)"]
604 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
605 | zstd = ["zstandard (>=0.18.0)"]
606 |
607 | [metadata]
608 | lock-version = "2.0"
609 | python-versions = "^3.8"
610 | content-hash = "dca740debd3d360d5a093b18871ce9cfbcc40c4ff40e1ff1193d9c8976bbd1cf"
611 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "conceptnet-lite"
3 | version = "0.2.0"
4 | description = "Python library to work with ConceptNet offline without the need of PostgreSQL"
5 | authors = ["Roman Inflianskas "]
6 | license = "Apache-2.0"
7 | readme = "README.md"
8 | repository = "https://github.com/ldtoolkit/conceptnet-lite"
9 | classifiers = ["Development Status :: 7 - Inactive"]
10 |
11 | [tool.poetry.dependencies]
12 | python = "^3.8"
13 | lmdb = "^1.0"
14 | peewee = "^3.10"
15 | tqdm = "^4.35"
16 | pysmartdl = "^1.3"
17 |
18 | [tool.poetry.dev-dependencies]
19 | sphinx = "^2.2"
20 | sphinx-rtd-theme = "^0.4.3"
21 | sphinx-autodoc-typehints = "^1.7"
22 |
23 | [build-system]
24 | requires = ["poetry>=0.12"]
25 | build-backend = "poetry.masonry.api"
26 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from distutils.core import setup
3 |
4 | packages = \
5 | ['conceptnet_lite']
6 |
7 | package_data = \
8 | {'': ['*']}
9 |
10 | install_requires = \
11 | ['lmdb>=0.97.0,<0.98.0',
12 | 'peewee>=3.10,<4.0',
13 | 'pysmartdl>=1.3,<2.0',
14 | 'tqdm>=4.35,<5.0']
15 |
16 | setup_kwargs = {
17 | 'name': 'conceptnet-lite',
18 | 'version': '0.1.27',
19 | 'description': 'Python library to work with ConceptNet offline without the need of PostgreSQL',
20 | 'long_description': '# conceptnet-lite\n[](https://www.apache.org/licenses/LICENSE-2.0)\n\n[](https://pypi.org/project/conceptnet-lite/)\n[](http://conceptnet-lite.readthedocs.io/en/latest/)\n\nConceptnet-lite is a Python library for working with ConceptNet offline without the need for PostgreSQL.\n\nThe library comes with Apache License 2.0, and is separate from ConceptNet itself. The ConceptNet is available under [CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/) license, which also applies to the formatted database file that we provide. See [here](https://github.com/commonsense/conceptnet5/wiki/Copying-and-sharing-ConceptNet) for the list of conditions for using ConceptNet data.\n\nThis is the official citation for ConceptNet if you use it in research:\n\n> Robyn Speer, Joshua Chin, and Catherine Havasi. 2017. "ConceptNet 5.5: An Open Multilingual Graph of General Knowledge." In proceedings of AAAI 31.\n\n## Installation\n\nTo install `conceptnet-lite` use `pip`:\n\n```shell\n$ pip install conceptnet-lite\n```\n\n## Connecting to the database\n\nBefore you can use `conceptnet-lite`, you will need to obtain ConceptNet dabase file. You have two options: download pre-made one or build it yourself from the raw ConceptNet assertions file.\n\n### Downloading the ConceptNet database \n\nConceptNet releases happen once a year. You can use `conceptnet-lite` to build your own database from the raw assertions file (see below), but if there is a pre-built file it will be faster to just get that one. `conceptnet-lite` can download and unpack it to the specified folder automatically.\n\nHere is a [link](https://conceptnet-lite.fra1.cdn.digitaloceanspaces.com/conceptnet.db.zip) to a compressed database for ConceptNet 5.7. This link is used automatically if you do not supply the alternative.\n\n```python\nimport conceptnet_lite\n\nconceptnet_lite.connect("/path/to/conceptnet.db")\n```\n\nThis command both downloads the resource (our build for ConceptNet 5.7) and connects to the database. If path specified as the first argument does not exist, it will be created (unless there is a permissions problem). Note that the database file is quite large (over 9 Gb). \n\nIf your internet connection is intermittent, the built-in download function may give you errors. If so, just download the file separately, unpack it to the directory of your choice and provide the path to the `.connect()` method as described below.\n\n### Building the database for a new release.\n\nIf a database file is not found in the folder specified in the `db_path` argument, `conceptnet-lite` will attempt to automatically download the raw assertions file from [here](https://github.com/commonsense/conceptnet5/wiki/Downloads) and build the database. This takes a couple of hours, so we recommend getting the pre-built file.\n\nIf you provide a path, this is where the database will be built. Note that the database file is quite large (over 9 Gb). Note that you need to pass `db_download_url=None` to force the library build the database from dump.\n\n```python\nimport conceptnet_lite\n\nconceptnet_lite.connect("/path/to/conceptnet.db", db_download_url=None)\n```\n\nIf the specified does not exist, it will be created (unless there is a permissions problem). If no path is specified, and no database file is not found in the current working directory, `conceptnet-lite` will attempt to build one in the current working directory. \n\nOnce the database is built, `conceptnet-lite` will connect to it automatically.\n\n### Loading the ConceptNet database \n\nOnce you have the database file, all you need to do is to pass the path to it to the `.connect()` method.\n\n```python\nimport conceptnet_lite\n\nconceptnet_lite.connect("/path/to/conceptnet.db")\n```\n\nIf no path is specified, `conceptnet-lite` will check if a database file exists in the current working directory. If it is not found, it will trigger the process of downloading the pre-built database (see above).\n\n## Accessing concepts\n\nConcepts objects are created by looking for every entry that matches the input string exactly.\nIf none is found, the `peewee.DoesNotExist` exception will be raised.\n\n```python\nfrom conceptnet_lite import Label\n\ncat_concepts = Label.get(text=\'cat\').concepts \nfor c in cat_concepts:\n print(" Concept URI:", c.uri)\n print(" Concept text:", c.text)\n```\n```console\nConcept URI: /c/en/cat\nConcept text: cat\nConcept URI: /c/en/cat/n\nConcept text: cat\nConcept URI: /c/en/cat/n/wn/animal\nConcept text: cat\nConcept URI: /c/en/cat/n/wn/person\n...\n```\n\n`concept.uri` provides access to ConceptNet URIs, as described [here](https://github.com/commonsense/conceptnet5/wiki/URI-hierarchy). You can also retrieve only the text of the entry by `concept.text`.\n\n## Working with languages\n\nYou can limit the languages to search for matches. Label.get() takes an optional `language` attribute that is expected to be an instance `Language`, which in turn is created by calling `Language.get()` with `name` argument.\nList of available languages and their codes are described [here](https://github.com/commonsense/conceptnet5/wiki/Languages).\n\n```python\nfrom conceptnet_lite import Label\n\ncat_concepts = Label.get(text=\'cat\', language=\'en\').concepts \nfor c in cat_concepts:\n print(" Concept URI:", c.uri)\n print(" Concept text:", c.text)\n print(" Concept language:", c.language.name)\n```\n\n```console\n Concept URI: /c/en/cat\n Concept text: cat\n Concept language: en\n Concept URI: /c/en/cat/n\n Concept text: cat\n Concept language: en\n Concept URI: /c/en/cat/n/wn/animal\n Concept text: cat\n Concept language: en\n Concept URI: /c/en/cat/n/wn/person\n Concept text: cat\n Concept language: en\n...\n```\n\n\n## Querying edges between concepts\n\nTo retrieve the set of relations between two concepts, you need to create the concept objects (optionally specifying the language as described above). `cn.edges_between()` method retrieves all edges between the specified concepts. You can access its URI and a number of attributes, as shown below.\n\nSome ConceptNet relations are symmetrical: for example, the antonymy between *white* and *black* works both ways. Some relations are asymmetrical: e.g. the relation between *cat* and *mammal* is either hyponymy or hyperonymy, depending on the direction. The `two_way` argument lets you choose whether the query should be symmetrical or not.\n\n```python\nfrom conceptnet_lite import Label, edges_between\n\nintrovert_concepts = Label.get(text=\'introvert\', language=\'en\').concepts\nextrovert_concepts = Label.get(text=\'extrovert\', language=\'en\').concepts\nfor e in edges_between(introvert_concepts, extrovert_concepts, two_way=False):\n print(" Edge URI:", e.uri)\n print(" Edge name:", e.relation.name)\n print(" Edge start node:", e.start.text)\n print(" Edge end node:", e.end.text)\n print(" Edge metadata:", e.etc)\n```\n```console\n Edge URI: /a/[/r/antonym/,/c/en/introvert/n/,/c/en/extrovert/]\n Edge name: antonym\n Edge start node: introvert\n Edge end node: extrovert\n Edge metadata: {\'dataset\': \'/d/wiktionary/en\', \'license\': \'cc:by-sa/4.0\', \'sources\': [{\'contributor\': \'/s/resource/wiktionary/en\', \'process\': \'/s/process/wikiparsec/2\'}, {\'contributor\': \'/s/resource/wiktionary/fr\', \'process\': \'/s/process/wikiparsec/2\'}], \'weight\': 2.0}\n```\n\n* **e.relation.name**: the name of ConceptNet relation. Full list [here](https://github.com/commonsense/conceptnet5/wiki/Relations).\n\n* **e.start.text, e.end.text**: the source and the target concepts in the edge\n\n* **e.etc**: the ConceptNet [metadata](https://github.com/commonsense/conceptnet5/wiki/Edges) dictionary contains the source dataset, sources, weight, and license. For example, the introvert:extrovert edge for English contains the following metadata:\n\n```json\n{\n\t"dataset": "/d/wiktionary/en",\n\t"license": "cc:by-sa/4.0",\n\t"sources": [{\n\t\t"contributor": "/s/resource/wiktionary/en",\n\t\t"process": "/s/process/wikiparsec/2"\n\t}, {\n\t\t"contributor": "/s/resource/wiktionary/fr",\n\t\t"process": "/s/process/wikiparsec/2"\n\t}],\n\t"weight": 2.0\n}\n```\n\n## Accessing all relations for a given concepts\n\nYou can also retrieve all relations between a given concepts and all other concepts, with the same options as above:\n\n```python\nfrom conceptnet_lite import Label, edges_for\n\nfor e in edges_for(Label.get(text=\'introvert\', language=\'en\').concepts, same_language=True):\n print(e.start.text, "::", e.end.text, "|", e.relation.name)\n```\n```console\nextrovert :: introvert | antonym\nintrovert :: extrovert | antonym\noutrovert :: introvert | antonym\nreflection :: introvert | at_location\nintroverse :: introvert | derived_from\nintroversible :: introvert | derived_from\nintroversion :: introvert | derived_from\nintroversion :: introvert | derived_from\nintroversive :: introvert | derived_from\nintroverted :: introvert | derived_from\n...\n```\n\nThe same set of edge attributes are available for `edges_between` and `edges_for` (e.uri, e.relation.name, e.start.text, e.end.text, e.etc).\n\nNote that we have used optional argument `same_language=True`. By supplying this argument we make `edges_for` return\nrelations, both ends of which are in the same language. If this argument is skipped it is possible to get edges to\nconcepts in languages other than the source concepts language. For example, the same command as above with `same_language=False` will include the following in the output:\n\n```console\nkääntyä_sisäänpäin :: introvert | synonym\nsulkeutua :: introvert | synonym\nsulkeutunut :: introvert | synonym\nintroverti :: introvert | synonym\nasociale :: introvert | synonym\nintroverso :: introvert | synonym\nintrovertito :: introvert | synonym\n内向 :: introvert | synonym\n```\n\n## Accessing concept edges with a given relation direction\n\nYou can also query the relations that have a specific concept as target or source. This is achieved with `concept.edges_out` and `concept.edges_in`, as follows:\n\n```python\nfrom conceptnet_lite import Label\n\nconcepts = Label.get(text=\'introvert\', language=\'en\').concepts \nfor c in concepts:\n print(" Concept text:", c.text)\n if c.edges_out:\n print(" Edges out:")\n for e in c.edges_out:\n print(" Edge URI:", e.uri)\n print(" Relation:", e.relation.name)\n print(" End:", e.end.text)\n if c.edges_in:\n print(" Edges in:")\n for e in c.edges_in:\n print(" Edge URI:", e.uri)\n print(" Relation:", e.relation.name)\n print(" End:", e.end.text)\n```\n```console\n Concept text: introvert\n Edges out:\n Edge URI: /a/[/r/etymologically_derived_from/,/c/en/introvert/,/c/la/introvertere/]\n Relation: etymologically_derived_from\n End: introvertere\n...\n Edges in:\n Edge URI: /a/[/r/antonym/,/c/cs/extrovert/n/,/c/en/introvert/]\n Relation: antonym\n End: introvert\n...\n```\n\n## Traversing all the data for a language\n\nYou can go over all concepts for a given language. For illustration, let us try Old Norse, a "small" language with the code "non" and vocab size of 7868, according to the [ConceptNet language statistics](https://github.com/commonsense/conceptnet5/wiki/Languages).\n\n```python\nfrom conceptnet_lite import Language\n\nmylanguage = Language.get(name=\'non\')\nfor l in mylanguage.labels:\n print(" Label:", l.text)\n for c in l.concepts:\n print(" Concept URI:", c.uri)\n if c.edges_out:\n print(" Edges out:")\n for e in c.edges_out:\n print(" Edge URI:", e.uri)\n if c.edges_in:\n print(" Edges in:")\n for e in c.edges_in:\n print(" Edge URI:", e.uri)\n```\n```console\n Label: andsœlis\n Concept URI: /c/non/andsœlis/r\n Edges out:\n Edge URI: /a/[/r/antonym/,/c/non/andsœlis/r/,/c/non/réttsœlis/]\n Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/against/]\n Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/course/]\n Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/sun/]\n Edge URI: /a/[/r/related_to/,/c/non/andsœlis/r/,/c/en/widdershins/]\n Edge URI: /a/[/r/synonym/,/c/non/andsœlis/r/,/c/non/rangsœlis/]\n Concept URI: /c/non/andsœlis\n Edges out:\n Edge URI: /a/[/r/external_url/,/c/non/andsœlis/,/c/en.wiktionary.org/wiki/andsœlis/]\n Label: réttsœlis\n Concept URI: /c/non/réttsœlis\n Edges in:\n Edge URI: /a/[/r/antonym/,/c/non/andsœlis/r/,/c/non/réttsœlis/]\n...\n```\n\n## Accessing Concepts by URI\n\nYou can access concept ORM objects directly by providing a desired ConceptNet URI. This is done as follows:\n\n```python\nfrom conceptnet_lite import Concept\n\nedge_object = Edge.get(start=\'/c/en/example\')\nconcept_object = Concept.get(uri=\'/c/en/example\')\n```\n',
21 | 'author': 'Roman Inflianskas',
22 | 'author_email': 'infroma@gmail.com',
23 | 'url': 'https://github.com/ldtoolkit/conceptnet-lite',
24 | 'packages': packages,
25 | 'package_data': package_data,
26 | 'install_requires': install_requires,
27 | 'python_requires': '>=3.6,<4.0',
28 | }
29 |
30 |
31 | setup(**setup_kwargs)
32 |
--------------------------------------------------------------------------------