├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── config.yml └── workflows │ ├── add-stars.yml │ └── build.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── benchmark.py ├── docs ├── index.html └── nimdoc.out.css ├── examples ├── .env ├── example_chmod.py ├── example_cwd.py ├── example_dotenv.py ├── example_expanduser.py ├── example_get_exe.py ├── example_get_file_info.py ├── example_get_file_times.py ├── example_get_size.py ├── example_home.py ├── example_is_file.py ├── example_is_valid_path.py ├── example_joinpath.py ├── example_lines.py ├── example_mkdir.py ├── example_path_splitted.py ├── example_replaced.py ├── example_resolve.py ├── example_rmdir.py ├── example_tokenized.py ├── example_walk.py ├── example_walk_glob.py ├── example_with_name.py ├── example_with_stem.py └── example_with_suffix.py ├── package4pypi.sh ├── results_graph.png ├── run-benchmark.sh ├── setup.py ├── thatlib-logo.jpg ├── thatlib ├── thatlib.nim ├── thatlib.nim.cfg ├── thatlib.nimble └── thatlib.py └── upload2pypi.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* linguist-language=Python 2 | * linguist-language=Python 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://gist.github.com/juancarlospaco/37da34ed13a609663f55f4466c4dbc3e"] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: "BUG Report" 2 | description: "Create a new Bug report." 3 | title: "[bug] " 4 | labels: ["unconfirmed"] 5 | assignees: ["juancarlospaco"] 6 | body: 7 | 8 | - type: markdown 9 | attributes: 10 | value: | 11 | - **Remember to :star: Star the project on GitHub!.** 12 | - **Please provide a minimal code example that reproduces the :bug: Bug!.** 13 | Reports with full repro code and descriptive detailed information will be fixed faster. 14 | - [Please, keep in mind there is ZERO FUNDING for the project!, we have no sponsors, no company behind, no dev team, 15 | :heart: Send crypto today to speed up development!](https://gist.github.com/juancarlospaco/37da34ed13a609663f55f4466c4dbc3e) 16 | 17 | - type: dropdown 18 | id: architecture 19 | attributes: 20 | label: Architecture 21 | description: What is your Hardware Architecture?. 22 | options: 23 | - x86_64 (Default) 24 | - x86_32 (32Bit) 25 | - ARM_64 (64Bit) 26 | - ARM_32 (32Bit) 27 | - AVR (Arduino, ESP32) 28 | - RISC (RISC-V) 29 | - Others (Unkown) 30 | validations: 31 | required: true 32 | 33 | - type: dropdown 34 | id: os 35 | attributes: 36 | label: Operating System 37 | description: What is your Operating System?. 38 | options: 39 | - Linux 40 | - Windows 41 | - Mac OSX 42 | - Android 43 | - BSD 44 | - FreeDOS 45 | - ReactOS 46 | - Others (Unkown) 47 | validations: 48 | required: true 49 | 50 | - type: dropdown 51 | id: disk 52 | attributes: 53 | label: Disk 54 | description: What is your main Disk Storage?. 55 | options: 56 | - SSD (Solid) 57 | - HDD (SATA, IDE, Mechanical) 58 | - NVME (M2, MSATA) 59 | - Others (USB) 60 | validations: 61 | required: true 62 | 63 | - type: dropdown 64 | id: ram 65 | attributes: 66 | label: Memory 67 | description: What is your total RAM Memory capacity?. 68 | options: 69 | - 1 Gigabytes 70 | - 2 Gigabytes 71 | - 4 Gigabytes 72 | - 8 Gigabytes 73 | - 16 Gigabytes 74 | - 32 Gigabytes 75 | - 64 Gigabytes 76 | - 128 Gigabytes 77 | - 256 Gigabytes 78 | - 512 Gigabytes 79 | - Others (Unkown) 80 | validations: 81 | required: true 82 | 83 | - type: dropdown 84 | id: cores 85 | attributes: 86 | label: CPU Cores 87 | description: What is your total CPU Cores count?. 88 | options: 89 | - 1 CPU Cores 90 | - 2 CPU Cores 91 | - 4 CPU Cores 92 | - 8 CPU Cores 93 | - 16 CPU Cores 94 | - 32 CPU Cores 95 | - 64 CPU Cores 96 | - 128 CPU Cores 97 | - 256 CPU Cores 98 | - 512 CPU Cores 99 | - Others (Unkown) 100 | validations: 101 | required: true 102 | 103 | - type: dropdown 104 | id: internet 105 | attributes: 106 | label: Internet Connection 107 | description: What is your Internet connection?. 108 | options: 109 | - Optical Fiber (very fast) 110 | - DSL (aDSL, DSL, etc) 111 | - Wifi (WLAN, Wireless) 112 | - LAN (RJ45, Local, etc) 113 | - Satellite (StarLink, etc) 114 | - Mobile (4G, 3G, Edge, etc) 115 | - Offline (No Internet) 116 | - Others (Unkown) 117 | validations: 118 | required: true 119 | 120 | - type: dropdown 121 | id: browser 122 | attributes: 123 | label: What is your web browser? 124 | options: 125 | - Chrome/Chromium 126 | - Firefox/Firefox Fork 127 | - Apple Safari 128 | - Microsoft Edge 129 | - KDE (Konqueror, Falkon, etc) 130 | - Others (Unkown) 131 | validations: 132 | required: true 133 | 134 | - type: dropdown 135 | id: device 136 | attributes: 137 | label: Device 138 | description: What kind of computer is it?. 139 | options: 140 | - Desktop PC 141 | - Server PC 142 | - Docker/Qemu (Container) 143 | - VirtualBox/Vagrant (Virtual Machine) 144 | - Embedded/IOT 145 | - Arduino/ESP32 Kit 146 | - SmartTV/SmartDisplay 147 | - Drone/Robot 148 | - ASIC/FPGA/Crypto-mining hardware 149 | - PLC/Industrial/heavy machine 150 | - Point Of Sale/Kiosk/ATM 151 | - Car/Self-Driving/On-Board Computer 152 | - Electric scooter/Electric bike 153 | - Satellite/MicroSatellite 154 | - Military machine 155 | - Others (Unkown) 156 | validations: 157 | required: true 158 | 159 | - type: dropdown 160 | id: country 161 | attributes: 162 | label: Where are you from? 163 | options: 164 | - Afghanistan 165 | - Albania 166 | - Algeria 167 | - Andorra 168 | - Angola 169 | - Antigua 170 | - Argentina 171 | - Armenia 172 | - Australia 173 | - Austria 174 | - Azerbaijan 175 | - Bahamas 176 | - Bahrain 177 | - Bangladesh 178 | - Barbados 179 | - Belarus 180 | - Belgium 181 | - Belize 182 | - Benin 183 | - Bhutan 184 | - Bolivia 185 | - Bosnia Herzegovina 186 | - Botswana 187 | - Brazil 188 | - Brunei 189 | - Bulgaria 190 | - Burkina 191 | - Burundi 192 | - Cambodia 193 | - Cameroon 194 | - Canada 195 | - Cape Verde 196 | - Central African Republic 197 | - Chad 198 | - Chile 199 | - China 200 | - Colombia 201 | - Comoros 202 | - Congo 203 | - Congo Democratic Republic 204 | - Costa Rica 205 | - Croatia 206 | - Cuba 207 | - Curacao 208 | - Cyprus 209 | - Czech Republic 210 | - Denmark 211 | - Djibouti 212 | - Dominica 213 | - Dominican Republic 214 | - East Timor 215 | - Ecuador 216 | - Egypt 217 | - El Salvador 218 | - Equatorial Guinea 219 | - Eritrea 220 | - Estonia 221 | - Ethiopia 222 | - Fiji 223 | - Finland 224 | - France 225 | - Gabon 226 | - Gambia 227 | - Georgia 228 | - Germany 229 | - Ghana 230 | - Greece 231 | - Grenada 232 | - Guatemala 233 | - Guinea 234 | - Guinea-Bissau 235 | - Guyana 236 | - Haiti 237 | - Honduras 238 | - Hungary 239 | - Iceland 240 | - India 241 | - Indonesia 242 | - Iran 243 | - Iraq 244 | - Ireland 245 | - Israel 246 | - Italy 247 | - Ivory Coast 248 | - Jamaica 249 | - Japan 250 | - Jordan 251 | - Kazakhstan 252 | - Kenya 253 | - Kiribati 254 | - Korea North 255 | - Korea South 256 | - Kosovo 257 | - Kuwait 258 | - Kyrgyzstan 259 | - Laos 260 | - Latvia 261 | - Lebanon 262 | - Lesotho 263 | - Liberia 264 | - Libya 265 | - Liechtenstein 266 | - Lithuania 267 | - Luxembourg 268 | - Macedonia 269 | - Madagascar 270 | - Malawi 271 | - Malaysia 272 | - Maldives 273 | - Mali 274 | - Malvinas Argentinas Islands 275 | - Malta 276 | - Marshall Islands 277 | - Mauritania 278 | - Mauritius 279 | - Mexico 280 | - Micronesia 281 | - Moldova 282 | - Monaco 283 | - Mongolia 284 | - Montenegro 285 | - Morocco 286 | - Mozambique 287 | - Myanmar 288 | - Namibia 289 | - Nauru 290 | - Nepal 291 | - Netherlands 292 | - New Zealand 293 | - Nicaragua 294 | - Niger 295 | - Nigeria 296 | - Norway 297 | - Oman 298 | - Pakistan 299 | - Palau 300 | - Palestine 301 | - Panama 302 | - Papua New Guinea 303 | - Paraguay 304 | - Peru 305 | - Philippines 306 | - Poland 307 | - Portugal 308 | - Qatar 309 | - Romania 310 | - Russia 311 | - Rwanda 312 | - St Kitts 313 | - St Lucia 314 | - Saint Vincent 315 | - Samoa 316 | - San Marino 317 | - Sao Tome Principe 318 | - Saudi Arabia 319 | - Senegal 320 | - Scotland 321 | - Serbia 322 | - Seychelles 323 | - Sierra Leone 324 | - Singapore 325 | - Slovakia 326 | - Slovenia 327 | - Solomon Islands 328 | - Somalia 329 | - South Africa 330 | - South Sudan 331 | - Spain 332 | - Sri Lanka 333 | - Sudan 334 | - Suriname 335 | - Swaziland 336 | - Sweden 337 | - Switzerland 338 | - Syria 339 | - Taiwan 340 | - Tajikistan 341 | - Tanzania 342 | - Thailand 343 | - Togo 344 | - Tonga 345 | - Trinidad Tobago 346 | - Tunisia 347 | - Turkey 348 | - Turkmenistan 349 | - Tuvalu 350 | - Uganda 351 | - Ukraine 352 | - United Arab Emirates 353 | - United Kingdom 354 | - United States 355 | - Uruguay 356 | - Uzbekistan 357 | - Vanuatu 358 | - Vatican City 359 | - Venezuela 360 | - Vietnam 361 | - Yemen 362 | - Zambia 363 | - Zimbabwe 364 | validations: 365 | required: true 366 | 367 | - type: textarea 368 | id: what-happened 369 | attributes: 370 | label: What happened? 371 | description: Use DETAILED DESCRIPTIVE information about the problem 372 | placeholder: Bug reports with full repro code and detailed information will be fixed faster. 373 | validations: 374 | required: true 375 | 376 | - type: textarea 377 | id: logs 378 | attributes: 379 | label: Standard Output Logs 380 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 381 | placeholder: Bug reports with full repro code and detailed information will be fixed faster. 382 | render: shell 383 | 384 | - type: markdown 385 | attributes: 386 | value: | 387 | **Before you open a new bug...** 388 | - 32-Bit is NOT supported. 389 | - Windows older than Windows 10 is NOT supported. 390 | - Mac OSX support is Experimental. 391 | - ARM support is Experimental. 392 | - Alpine Linux support is Experimental. 393 | - Termux support is Experimental. 394 | - Check if Git main branch already have a fix for your problem. 395 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Sponsor this Project 3 | url: https://gist.github.com/juancarlospaco/37da34ed13a609663f55f4466c4dbc3e 4 | about: Toss a coin to your witcher... 5 | -------------------------------------------------------------------------------- /.github/workflows/add-stars.yml: -------------------------------------------------------------------------------- 1 | name: Add Stars 2 | 3 | on: [watch] 4 | 5 | jobs: 6 | addstars: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Add Stars to Readme 11 | run: | 12 | echo -e ":star: [@${{github.actor}}](https://github.com/${{github.actor}} '`date --iso-8601`')\t" >> README.md 13 | 14 | - name: Commit changes 15 | uses: elstudio/actions-js-build/commit@v2 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | PUSH_BRANCH: 'nim' 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | paths: 5 | - 'setup.py' 6 | - 'setup.cfg' 7 | - 'PKG-INFO' 8 | - 'thatlib/*.*' 9 | - '.github/workflows/*.yml' 10 | - '.github/workflows/*.yaml' 11 | - '.gitignore' 12 | 13 | jobs: 14 | build: 15 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 16 | strategy: 17 | fail-fast: true 18 | matrix: 19 | platform: [ubuntu-latest, windows-latest] # Mac too slow in CI. 20 | name: ${{ matrix.platform }} 21 | runs-on: ${{ matrix.platform }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.9' 27 | 28 | - uses: jiro4989/setup-nim-action@v1 29 | with: 30 | nim-version: '1.4.8' 31 | no-color: true # --noColor 32 | 33 | - name: Set Environment Variables 34 | uses: allenevans/set-env@v2.0.0 35 | with: 36 | NIMPORTER_INSTRUMENT: true 37 | PACKAGE_NAME: "thatlib" 38 | MAIN_MODULE: "thatlib/thatlib.nim" 39 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 40 | 41 | 42 | - name: Nimble 43 | run: | 44 | nimble -y refresh 45 | nimble -y install nimpy 46 | 47 | 48 | - uses: juancarlospaco/nimpretty-action@main 49 | with: 50 | folders: "thatlib" 51 | 52 | 53 | - name: Generate Documentation (Linux) 54 | if: runner.os == 'Linux' 55 | run: nim doc --out:docs/index.html ${{ env.MAIN_MODULE }} 56 | 57 | 58 | - name: Compile (Unix) 59 | if: runner.os == 'Linux' || runner.os == 'macOS' 60 | run: nim c --app:lib --gc:arc --experimental:strictFuncs --out:$PACKAGE_NAME.so $MAIN_MODULE 61 | 62 | 63 | - name: Compile (Windows) 64 | if: runner.os == 'Windows' 65 | run: nim c --app:lib --gc:arc --experimental:strictFuncs --out:${{ env.PACKAGE_NAME }}.pyd ${{ env.MAIN_MODULE }} 66 | 67 | 68 | - name: Install Nimporter 69 | shell: bash 70 | run: | 71 | git clone https://github.com/Pebaz/nimporter.git 72 | cd nimporter/ 73 | git checkout nimporter-v2.0.0rc 74 | python setup.py install 75 | cd .. 76 | rm --force --recursive nimporter/ 77 | 78 | 79 | - name: Install Thatlib 80 | run: | 81 | python setup.py install 82 | 83 | 84 | - name: Import Thatlib 85 | run: | 86 | python -c "import thatlib ; print(thatlib.cwd())" 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | *.dll 5 | *.so 6 | *.pyd 7 | __pycache__/ 8 | *.pyc 9 | *.c.o 10 | dist/*.zip 11 | thatlib/*.so 12 | results.csv 13 | thatlib/benchmark.py 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team. 59 | All complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome Contributors 3 | 4 | When contributing, please first discuss the change you wish to make via issue, before making the actual change. 5 | 6 | We want to make contributing to this project as easy and transparent as possible. 7 | 8 | Please note we have a Code Of Conduct, please follow it in all your interactions with the project. 9 | 10 | 11 | # FAQ 12 | 13 | Whats Nim? 14 | 15 | - Its just like Cython, but faster and more elegant syntax, we can say Nim is more Pythonic than Cython, 16 | is a proper programming language instead of an experimental pseudo-lang alternative syntax, it also runs on the Browser Frontend. 17 | 18 | 19 | # Development Process 20 | 21 | [We use Github Flow](https://guides.github.com/introduction/flow/index.html), so all code changes happen via Pull Requests: 22 | 23 | 1. Fork the repo from latest working `master` and create a new branch. 24 | 2. Change the code on the new branch, update the documentation, test it locally. 25 | 3. Send a new Pull Request!, follow the Feedback from the Peer Reviews so it gets Merged faster. 26 | 27 | 28 | # How to Contribute if you cant code 29 | 30 | - [Make Nim Fan Art and Memes](https://t.me/addstickers/nimlang). 31 | - Give a Talk about Nim-based Python libs at your local meetup. 32 | - Talk about Nim-based Python libs on your social networks. 33 | - [Writing tutorials and blog posts](https://dev.to/juancarlospaco/self-firejailing-web-framework-h5l). 34 | - Improving documentations. 35 | - UX and Design improvements. 36 | - Make Benchmarks of Nim-based libs Vs other frameworks. 37 | - Mention Nim-based Python libs on discussions about Python. 38 | - Submitting bugs and feature requests. 39 | 40 | Are all examples of very helpful contributions!. 41 | 42 | 43 | # Style Guide 44 | 45 | - Follow [NEP1](https://nim-lang.org/docs/nep1.html). 46 | - Keep API human-friendly, but at the same time use performant algos, thats the Nim way. 47 | - Try to use naming that makes sense :) 48 | 49 | 50 | # Your Responsibilities 51 | 52 | - Write detailed bug reports with sample code. 53 | - Write meaningful human-friendly Commit Messages. 54 | - Ensure Linux platform and Latest Stable Nim compatibility. 55 | - Discuss things transparently and get community feedback. 56 | - Do not add any Types to the codebase unless absolutely needed. 57 | - Prefer standard library as much as possible instead of third party modules. 58 | - Document how to use the new features and pros/cons if any. 59 | - Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Juan Carlos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ThatLib 2 | 3 | ![](https://raw.githubusercontent.com/juancarlospaco/thatlib/nim/thatlib-logo.jpg "Faster pathlib for Python") 4 | 5 | [![Benchmark Results](https://raw.githubusercontent.com/juancarlospaco/thatlib/nim/results_graph.png "Benchmark Results")](https://youtu.be/QiKwnlyhKrk?t=5) 6 | 7 | ![](https://img.shields.io/github/languages/top/juancarlospaco/thatlib?style=for-the-badge) 8 | ![](https://img.shields.io/github/languages/count/juancarlospaco/thatlib?logoColor=green&style=for-the-badge) 9 | ![](https://img.shields.io/github/stars/juancarlospaco/thatlib?style=for-the-badge) 10 | ![](https://img.shields.io/github/languages/code-size/juancarlospaco/thatlib?style=for-the-badge) 11 | ![](https://img.shields.io/github/issues-raw/juancarlospaco/thatlib?style=for-the-badge) 12 | ![](https://img.shields.io/github/issues-pr-raw/juancarlospaco/thatlib?style=for-the-badge) 13 | ![](https://img.shields.io/github/last-commit/juancarlospaco/thatlib?style=for-the-badge) 14 | 15 | 16 | # Using Pathlib can make your project +50x Slower 17 | 18 | [![Using Pathlib can make your project 50x Slower](https://img.youtube.com/vi/tFrh9hKMS6Y/0.jpg)](https://www.youtube.com/watch?v=tFrh9hKMS6Y) 19 | 20 | 21 | # Use 22 | 23 | - Same API as `pathlib`, nothing new to remember. 24 | - [See the examples folder (20+ simple tiny examples).](https://github.com/juancarlospaco/thatlib/tree/nim/examples) 25 | - https://juancarlospaco.github.io/thatlib 26 | 27 | 28 | # Type-safe DotEnv 29 | 30 | Thatlib has builtin support for Type-safe `.env`. Type-safe `.env` file is just a `.env` but Typed. 31 | 32 | Types are enforced via a comment, so it is still a "vanilla" `.env`. 33 | 34 | Type-safe `.env` file can be used with unsafe `.env` parsers, legacy parsers will ignore the comment. 35 | 36 | Keys must be a non-empty ASCII string `[a-zA-Z0-9_]`, keys are validated. Key-Value separator must be `=`. 37 | 38 | Parses the same `.env` file from the vanilla implementation tests. 39 | 40 | Several orders of magnitude faster than the vanilla implementation. Implementation is ~ 50 lines of code. 41 | 42 | Examples: 43 | 44 | ```ini 45 | # This is a comment 46 | DB_HOST=localhost # string 47 | DB_USER=root # string 48 | DB_PASS="123" # string 49 | DB_TIMEOUT=42 # int 50 | DELAY=3.14 # float 51 | ACTIVE=true # bool 52 | ``` 53 | 54 | - `.env` file example https://github.com/juancarlospaco/thatlib/blob/nim/examples/.env 55 | - Python use example https://github.com/juancarlospaco/thatlib/blob/nim/examples/example_dotenv.py 56 | 57 | 58 | # Requisites 59 | 60 | - Python 1.x or 2.x or 3.x, 64Bit, CPython implementation. 61 | 62 | 63 | # PYPI 64 | 65 | - https://pypi.org/project/thatlib 66 | 67 | 68 | # Dependencies 69 | 70 | - None. 71 | 72 | 73 | # 💰➡️🍕 74 | 75 |
76 | Bitcoin BTC 77 | 78 | **BEP20 Binance Smart Chain Network BSC** 79 | ``` 80 | 0xb78c4cf63274bb22f83481986157d234105ac17e 81 | ``` 82 | **BTC Bitcoin Network** 83 | ``` 84 | 1Pnf45MgGgY32X4KDNJbutnpx96E4FxqVi 85 | ``` 86 | **Lightning Network** 87 | ``` 88 | juancarlospaco@bitrefill.me 89 | ``` 90 |
91 | 92 |
93 | Ethereum ETH Dai DAI Uniswap UNI Axie Infinity AXS Smooth Love Potion SLP Uniswap UNI USDC 94 | 95 | **BEP20 Binance Smart Chain Network BSC** 96 | ``` 97 | 0xb78c4cf63274bb22f83481986157d234105ac17e 98 | ``` 99 | **ERC20 Ethereum Network** 100 | ``` 101 | 0xb78c4cf63274bb22f83481986157d234105ac17e 102 | ``` 103 |
104 |
105 | Tether USDT 106 | 107 | **BEP20 Binance Smart Chain Network BSC** 108 | ``` 109 | 0xb78c4cf63274bb22f83481986157d234105ac17e 110 | ``` 111 | **ERC20 Ethereum Network** 112 | ``` 113 | 0xb78c4cf63274bb22f83481986157d234105ac17e 114 | ``` 115 | **TRC20 Tron Network** 116 | ``` 117 | TWGft53WgWvH2mnqR8ZUXq1GD8M4gZ4Yfu 118 | ``` 119 |
120 |
121 | Solana SOL 122 | 123 | **BEP20 Binance Smart Chain Network BSC** 124 | ``` 125 | 0xb78c4cf63274bb22f83481986157d234105ac17e 126 | ``` 127 | **SOL Solana Network** 128 | ``` 129 | FKaPSd8kTUpH7Q76d77toy1jjPGpZSxR4xbhQHyCMSGq 130 | ``` 131 |
132 |
133 | Cardano ADA 134 | 135 | **BEP20 Binance Smart Chain Network BSC** 136 | ``` 137 | 0xb78c4cf63274bb22f83481986157d234105ac17e 138 | ``` 139 | **ADA Cardano Network** 140 | ``` 141 | DdzFFzCqrht9Y1r4Yx7ouqG9yJNWeXFt69xavLdaeXdu4cQi2yXgNWagzh52o9k9YRh3ussHnBnDrg7v7W2hSXWXfBhbo2ooUKRFMieM 142 | ``` 143 |
144 |
145 | Sandbox SAND Decentraland MANA 146 | 147 | **ERC20 Ethereum Network** 148 | ``` 149 | 0xb78c4cf63274bb22f83481986157d234105ac17e 150 | ``` 151 |
152 |
153 | Algorand ALGO 154 | 155 | **ALGO Algorand Network** 156 | ``` 157 | WM54DHVZQIQDVTHMPOH6FEZ4U2AU3OBPGAFTHSCYWMFE7ETKCUUOYAW24Q 158 | ``` 159 |
160 |
161 | Polkadot DOT 162 | 163 | **DOT Network** 164 | ``` 165 | 13GdxHQbQA1K6i7Ctf781nQkhQhoVhGgUnrjn9EvcJnYWCEd 166 | ``` 167 | **BEP20 Binance Smart Chain Network BSC** 168 | ``` 169 | 0xb78c4cf63274bb22f83481986157d234105ac17e 170 | ``` 171 |
172 |
173 | Binance 174 | 175 | [https://pay.binance.com/en/checkout/e92e536210fd4f62b426ea7ee65b49c3](https://pay.binance.com/en/checkout/e92e536210fd4f62b426ea7ee65b49c3 "Send via Binance Pay") 176 |
177 | 178 | 179 | # Stars 180 | 181 | ![](https://starchart.cc/juancarlospaco/thatlib.svg) 182 | :star: [@juancarlospaco](https://github.com/juancarlospaco '2022-02-16') 183 | :star: [@hamidb80](https://github.com/hamidb80 '2022-02-16') 184 | :star: [@nikitavoloboev](https://github.com/nikitavoloboev '2022-02-16') 185 | :star: [@mxschmitt](https://github.com/mxschmitt '2022-02-17') 186 | :star: [@harrtho](https://github.com/harrtho '2022-02-17') 187 | :star: [@kangkot](https://github.com/kangkot '2022-04-12') 188 | :star: [@crox-safe](https://github.com/crox-safe '2022-04-13') 189 | :star: [@carno](https://github.com/carno '2022-06-01') 190 | :star: [@xdroff](https://github.com/xdroff '2022-06-01') 191 | :star: [@tumregels](https://github.com/tumregels '2022-06-18') 192 | :star: [@Pebaz](https://github.com/Pebaz '2022-07-17') 193 | :star: [@Siss3l](https://github.com/Siss3l '2022-11-19') 194 | :star: [@daweedkob](https://github.com/daweedkob '2022-11-19') 195 | :star: [@xilicode](https://github.com/xilicode '2022-11-25') 196 | :star: [@pietroppeter](https://github.com/pietroppeter '2023-01-20') 197 | :star: [@linwaytin](https://github.com/linwaytin '2023-01-22') 198 | :star: [@thomas-mckay](https://github.com/thomas-mckay '2023-06-09') 199 | :star: [@hansalemaos](https://github.com/hansalemaos '2023-08-07') 200 | :star: [@benzolium](https://github.com/benzolium '2023-08-09') 201 | :star: [@T145](https://github.com/T145 '2023-08-13') 202 | :star: [@Xynonners](https://github.com/Xynonners '2023-08-13') 203 | :star: [@BlackNurse](https://github.com/BlackNurse '2023-08-19') 204 | :star: [@xjzh123](https://github.com/xjzh123 '2023-10-11') 205 | :star: [@JoeBarouneD](https://github.com/JoeBarouneD '2023-12-17') 206 | :star: [@wrath-codes](https://github.com/wrath-codes '2025-01-12') 207 | :star: [@wrath-codes](https://github.com/wrath-codes '2025-03-14') 208 | -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import timeit 5 | import time 6 | import string 7 | import argparse 8 | import csv 9 | import pandas 10 | 11 | 12 | def run_test(library, repetitions, setup_test, run_test): 13 | mytime = timeit.timeit(stmt=run_test, setup=setup_test, number=repetitions) 14 | print(f"{library} =\t{round(mytime, 4)}") 15 | result = [library, repetitions, mytime] 16 | return result 17 | 18 | 19 | def run_all_benchmarks(repetitions=10_000, output_file="results.csv"): 20 | results = [] 21 | tests = [] 22 | 23 | tests.append(('pathlib cwd() ', 'import pathlib', "pathlib.Path().cwd()")) 24 | tests.append(('thatlib cwd() ', 'import thatlib', "thatlib.cwd()")) 25 | 26 | tests.append(('pathlib home()', 'import pathlib', "pathlib.Path().home()")) 27 | tests.append(('thatlib home()', 'import thatlib', "thatlib.home()")) 28 | 29 | tests.append(('pathlib is_file()', 'import pathlib', "pathlib.Path(__file__).is_file()")) 30 | tests.append(('thatlib is_file()', 'import thatlib', "thatlib.is_file(__file__)")) 31 | 32 | tests.append(('pathlib exists()', 'import pathlib', "pathlib.Path(__file__).exists()")) 33 | tests.append(('thatlib exists()', 'import thatlib', "thatlib.exists(__file__)")) 34 | 35 | tests.append(('pathlib is_absolute()', 'import pathlib', "pathlib.Path(__file__).is_absolute()")) 36 | tests.append(('thatlib is_absolute()', 'import thatlib', "thatlib.is_absolute(__file__)")) 37 | 38 | tests.append(('pathlib joinpath()', 'import pathlib', "pathlib.Path(__file__).joinpath('foo')")) 39 | tests.append(('thatlib joinpath()', 'import thatlib', "thatlib.joinpath([__file__, 'foo'])")) 40 | 41 | tests.append(('pathlib with_suffix()', 'import pathlib', "pathlib.Path(__file__).with_suffix('.foo')")) 42 | tests.append(('thatlib with_suffix()', 'import thatlib', "thatlib.with_suffix(__file__, '.foo')")) 43 | 44 | tests.append(('pathlib with_stem()', 'import pathlib', "pathlib.Path(__file__).with_stem('foo')")) 45 | tests.append(('thatlib with_stem()', 'import thatlib', "thatlib.with_stem(__file__, 'foo')")) 46 | 47 | tests.append(('pathlib with_name()', 'import pathlib', "pathlib.Path(__file__).with_name('foo')")) 48 | tests.append(('thatlib with_name()', 'import thatlib', "thatlib.with_name(__file__, 'foo')")) 49 | 50 | tests.append(('pathlib expanduser()', 'import pathlib', "pathlib.Path('~/foo').expanduser()")) 51 | tests.append(('thatlib expanduser()', 'import thatlib', "thatlib.expanduser('~/foo')")) 52 | 53 | tests.append(('pathlib joinpath()', 'import pathlib', "pathlib.Path('foo').joinpath('bar')")) 54 | tests.append(('thatlib joinpath()', 'import thatlib', "thatlib.joinpath(['foo', 'bar'])")) 55 | 56 | # tests.append(('pathlib write_bytes()', 'import pathlib', "pathlib.Path('/tmp/foo.txt').write_bytes(b'bar')")) 57 | # tests.append(('thatlib write_bytes()', 'import thatlib', "thatlib.write_bytes('/tmp/foo.txt', b'bar')")) 58 | 59 | # tests.append(('pathlib read_bytes()', 'import pathlib', "pathlib.Path('/tmp/foo.txt').read_bytes()")) 60 | # tests.append(('thatlib read_bytes()', 'import thatlib', "thatlib.read_bytes('/tmp/foo.txt')")) 61 | 62 | tests.append(('pathlib chmod()', 'import pathlib', "pathlib.Path('/tmp/foo.txt').chmod(0o666)")) 63 | tests.append(('thatlib chmod()', 'import thatlib', "thatlib.chmod('/tmp/foo.txt', 0o666)")) 64 | 65 | for test in tests: 66 | my_result = run_test(test[0], repetitions, test[1], test[2]) 67 | results.append((test[0], my_result[-1])) 68 | 69 | if output_file: 70 | with open(output_file, 'w') as csvfile: 71 | outwriter = csv.writer(csvfile, dialect=csv.excel) 72 | outwriter.writerow(('library', 'time')) 73 | for result in results: 74 | outwriter.writerow(result) 75 | 76 | 77 | def plot_benchmark_results(in_path="results.csv", output_path="results_graph.png"): 78 | data = pandas.read_csv(in_path) 79 | chart = data.plot.bar(x='library', y='time', figsize=(10, 4), color=["red", "green"], title="Pathlib Versus Thatlib", rot=85) 80 | chart.figure.tight_layout() 81 | chart.figure.savefig(output_path) 82 | return 83 | 84 | 85 | if __name__ == '__main__': 86 | parser = argparse.ArgumentParser(description="Benchmarks for path libs") 87 | parser.add_argument('--repetitions', metavar='c', type=int, default=10_000, help="Repetitions") 88 | args = vars(parser.parse_args()) 89 | assert args.get('repetitions') > 100, "Repetitions must be > 100." 90 | print(args) 91 | run_all_benchmarks(**args) 92 | plot_benchmark_results() 93 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | thatlib 21 | 22 | 23 | 24 | 25 | 67 | 68 | 69 | 70 |
71 |
72 |

thatlib

73 |
74 |
75 |
76 | 80 |     Dark Mode 81 |
82 | 89 |
90 | Search: 92 |
93 |
94 | Group by: 95 | 99 |
100 | 481 | 482 |
483 | 484 |
485 |
486 | 487 |

488 |
489 |

Procs

490 |
491 |
492 |
proc absolute(path: string): string {....raises: [ValueError, OSError], tags: [].}
493 |
494 | 495 | 496 | 497 |
498 |
499 |
500 |
proc as_uri(path: string): string {....raises: [ValueError, OSError], tags: [].}
501 |
502 | 503 | 504 | 505 |
506 |
507 |
508 |
proc chmod(path: string; permissions: uint) {....raises: [OSError],
 509 |     tags: [ReadDirEffect, WriteDirEffect].}
510 |
511 | 512 | 513 | 514 |
515 |
516 |
517 |
func compare_paths(pathA, pathB: string): int {....raises: [], tags: [].}
518 |
519 | 520 | 521 | 522 |
523 |
524 |
525 |
proc copy_dir_permissions(source, dest: string; ignore_errors: bool = true) {.
 526 |     ...raises: [OSError, IOError, Exception],
 527 |     tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect].}
528 |
529 | 530 | 531 | 532 |
533 |
534 |
535 |
proc copy_file_permissions(source, dest: string; ignore_errors: bool = true) {.
 536 |     ...raises: [OSError, IOError, Exception],
 537 |     tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect, WriteDirEffect].}
538 |
539 | 540 | 541 | 542 |
543 |
544 |
545 |
proc counted_lines(path: string): int {....raises: [IOError], tags: [ReadIOEffect].}
546 |
547 | 548 | 549 | 550 |
551 |
552 |
553 |
proc cwd(): string {....raises: [OSError], tags: [].}
554 |
555 | 556 | 557 | 558 |
559 |
560 |
561 |
proc dotenv(path: string): string {....raises: [IOError, OSError, JsonParsingError,
 562 |     ValueError, Exception], tags: [ReadIOEffect, WriteIOEffect].}
563 |
564 | 565 | 566 | 567 |
568 |
569 |
570 |
proc env_vars_pairs(): seq[(string, string)] {....raises: [], tags: [ReadEnvEffect].}
571 |
572 | 573 | 574 | 575 |
576 |
577 |
578 |
proc exists(path: string): bool {....raises: [], tags: [ReadDirEffect].}
579 |
580 | 581 | 582 | 583 |
584 |
585 |
586 |
proc exists_create_dir(path: string): bool {....raises: [OSError, IOError],
 587 |     tags: [WriteDirEffect, ReadDirEffect].}
588 |
589 | 590 | 591 | 592 |
593 |
594 |
595 |
proc expanduser(path: string): string {....raises: [],
 596 |                                         tags: [ReadEnvEffect, ReadIOEffect].}
597 |
598 | 599 | 600 | 601 |
602 |
603 |
604 |
proc get_conf_dir(): string {....raises: [], tags: [ReadEnvEffect, ReadIOEffect].}
605 |
606 | 607 | 608 | 609 |
610 |
611 |
612 |
func get_dynlib_format(): string {....raises: [], tags: [].}
613 |
614 | 615 | 616 | 617 |
618 |
619 |
620 |
proc get_exe(path: string; followSymlinks: bool = true): string {.
 621 |     ...raises: [OSError], tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].}
622 |
623 | 624 | 625 | 626 |
627 |
628 |
629 |
proc get_file_info(path: string; follow_symlinks: bool = true): tuple[
 630 |     size, link_count, block_size, permissions: int64] {....raises: [OSError],
 631 |     tags: [].}
632 |
633 | 634 | 635 | 636 |
637 |
638 |
639 |
proc get_file_times_iso(path: string; follow_symlinks: bool = true): tuple[
 640 |     last_access, last_write, creation: string] {....raises: [OSError], tags: [].}
641 |
642 | 643 | 644 | 645 |
646 |
647 |
648 |
proc get_file_times_unix(path: string; follow_symlinks: bool = true): tuple[
 649 |     last_access, last_write, creation: int64] {....raises: [OSError], tags: [].}
650 |
651 | 652 | 653 | 654 |
655 |
656 |
657 |
proc get_md5(path: string): string {....raises: [IOError], tags: [ReadIOEffect].}
658 |
659 | 660 | 661 | 662 |
663 |
664 |
665 |
proc get_sha1(path: string): string {....raises: [IOError], tags: [ReadIOEffect].}
666 |
667 | 668 | 669 | 670 |
671 |
672 |
673 |
proc get_size(path: string): BiggestInt {....raises: [IOError, OSError],
 674 |     tags: [ReadIOEffect].}
675 |
676 | 677 | 678 | 679 |
680 |
681 |
682 |
proc get_size_human(path: string): string {....raises: [IOError, OSError],
 683 |     tags: [ReadIOEffect].}
684 |
685 | 686 | 687 | 688 |
689 |
690 |
691 |
func get_suffix_index(path: string): int {....raises: [], tags: [].}
692 |
693 | 694 | 695 | 696 |
697 |
698 |
699 |
proc get_symlink(path: string): string {....raises: [OSError], tags: [].}
700 |
701 | 702 | 703 | 704 |
705 |
706 |
707 |
proc get_temporary_dir(): string {....raises: [],
 708 |                                    tags: [ReadEnvEffect, ReadIOEffect].}
709 |
710 | 711 | 712 | 713 |
714 |
715 |
716 |
proc hardlink(source, destination: string) {....raises: [OSError], tags: [].}
717 |
718 | 719 | 720 | 721 |
722 |
723 |
724 |
proc home(): string {....raises: [], tags: [ReadEnvEffect, ReadIOEffect].}
725 |
726 | 727 | 728 | 729 |
730 |
731 |
732 |
func is_absolute(path: string): bool {....raises: [], tags: [].}
733 |
734 | 735 | 736 | 737 |
738 |
739 |
740 |
proc is_dir(path: string): bool {....raises: [], tags: [ReadDirEffect].}
741 |
742 | 743 | 744 | 745 |
746 |
747 |
748 |
proc is_file(path: string): bool {....raises: [], tags: [ReadDirEffect].}
749 |
750 | 751 | 752 | 753 |
754 |
755 |
756 |
proc is_file_newer(pathA, pathB: string): bool {....raises: [OSError], tags: [].}
757 |
758 | 759 | 760 | 761 |
762 |
763 |
764 |
func is_fs_casesensitive(): bool {....raises: [], tags: [].}
765 |
766 | 767 | 768 | 769 |
770 |
771 |
772 |
proc is_hidden_path(path: string): bool {....raises: [], tags: [].}
773 |
774 | 775 | 776 | 777 |
778 |
779 |
780 |
proc is_relative_to(path, base: string): bool {....raises: [Exception],
 781 |     tags: [RootEffect].}
782 |
783 | 784 | 785 | 786 |
787 |
788 |
789 |
proc is_root(): bool {....raises: [], tags: [].}
790 |
791 | 792 | 793 | 794 |
795 |
796 |
797 |
func is_root_dir(path: string): bool {....raises: [], tags: [].}
798 |
799 | 800 | 801 | 802 |
803 |
804 |
805 |
func is_valid_path(path: string): bool {....raises: [], tags: [].}
806 |
807 | 808 | 809 | 810 |
811 |
812 |
813 |
func joinpath(paths: openArray[string]): string {....raises: [], tags: [].}
814 |
815 | 816 | 817 | 818 |
819 |
820 |
821 |
proc line(path: string; line_number: Natural): string {....raises: [IOError],
 822 |     tags: [ReadIOEffect].}
823 |
824 | 825 | 826 | 827 |
828 |
829 |
830 |
proc lines(path: string; start: int = 0; ends: int = 1): seq[string] {.
 831 |     ...raises: [IOError], tags: [ReadIOEffect].}
832 |
833 | 834 | 835 | 836 |
837 |
838 |
839 |
proc mkdir(path: string) {....raises: [OSError, IOError],
 840 |                            tags: [WriteDirEffect, ReadDirEffect].}
841 |
842 | 843 | 844 | 845 |
846 |
847 |
848 |
proc mkhardlink(source, destination: string) {....raises: [OSError], tags: [].}
849 |
850 | 851 | 852 | 853 |
854 |
855 |
856 |
proc mksymlink(source, destination: string): uint {....raises: [OSError], tags: [].}
857 |
858 | 859 | 860 | 861 |
862 |
863 |
864 |
func normalized(path: string): string {....raises: [], tags: [].}
865 |
866 | 867 | 868 | 869 |
870 |
871 |
872 |
func parent(path: string): string {....raises: [], tags: [].}
873 |
874 | 875 | 876 | 877 |
878 |
879 |
880 |
func parents(path: string): string {....raises: [], tags: [].}
881 |
882 | 883 | 884 | 885 |
886 |
887 |
888 |
proc parts(path: string): seq[string] {....raises: [ValueError, OSError], tags: [].}
889 |
890 | 891 | 892 | 893 |
894 |
895 |
896 |
func path_splitted(path: string): tuple[dir, name, ext: string] {....raises: [],
 897 |     tags: [].}
898 |
899 | 900 | 901 | 902 |
903 |
904 |
905 |
func paths_quoted(paths: openArray[string]): string {....raises: [], tags: [].}
906 |
907 | 908 | 909 | 910 |
911 |
912 |
913 |
proc read_bytes(path: string): string {....raises: [IOError], tags: [ReadIOEffect].}
914 |
915 | 916 | 917 | 918 |
919 |
920 |
921 |
proc rename(source, destination: string) {.
 922 |     ...raises: [OSError, IOError, Exception],
 923 |     tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].}
924 |
925 | 926 | 927 | 928 |
929 |
930 |
931 |
proc replace(source, destination: string): string {.
 932 |     ...raises: [OSError, IOError, Exception],
 933 |     tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].}
934 |
935 | 936 | 937 | 938 |
939 |
940 |
941 |
func replaced(path: string; replacements: openArray[(string, string)]): string {.
 942 |     ...raises: [], tags: [].}
943 |
944 | 945 | 946 | 947 |
948 |
949 |
950 |
proc resolve(path: string): string {....raises: [ValueError, OSError], tags: [].}
951 |
952 | 953 | 954 | 955 |
956 |
957 |
958 |
proc rmdir(path: string; check: bool = false) {....raises: [OSError],
 959 |     tags: [WriteDirEffect, ReadDirEffect].}
960 |
961 | 962 | 963 | 964 |
965 |
966 |
967 |
func samefile(pathA, pathB: string): bool {....raises: [], tags: [].}
968 |
969 | 970 | 971 | 972 |
973 |
974 |
975 |
func stem(path: string): string {....raises: [], tags: [].}
976 |
977 | 978 | 979 | 980 |
981 |
982 |
983 |
func suffix(path: string): string {....raises: [], tags: [].}
984 |
985 | 986 | 987 | 988 |
989 |
990 |
991 |
proc symlink(source, destination: string) {....raises: [OSError], tags: [].}
992 |
993 | 994 | 995 | 996 |
997 |
998 | 1006 |
1007 |
proc tokenized(path: string): seq[tuple[token: string, isSep: bool]] {.
1008 |     ...raises: [IOError], tags: [ReadIOEffect].}
1009 |
1010 | 1011 | 1012 | 1013 |
1014 |
1015 |
1016 |
proc try_rmfile(path: string): bool {....raises: [], tags: [WriteDirEffect].}
1017 |
1018 | 1019 | 1020 | 1021 |
1022 |
1023 |
1024 |
proc walk(folderpath: string; extensions: seq[string] = @[""];
1025 |           followlinks: bool = false; yieldfiles: bool = true;
1026 |           debugs: bool = false; check_folders: bool = false;
1027 |           prealloc: Positive = 99): seq[string] {....raises: [OSError, ValueError],
1028 |     tags: [ReadDirEffect, WriteIOEffect].}
1029 |
1030 | 1031 | 1032 | 1033 |
1034 |
1035 |
1036 |
proc walk_files(globpattern: string; prealloc: Positive = 99): seq[string] {.
1037 |     ...raises: [ValueError, OSError], tags: [ReadDirEffect].}
1038 |
1039 | 1040 | 1041 | 1042 |
1043 |
1044 |
1045 |
proc walk_folders(globpattern: string; prealloc: Positive = 99): seq[string] {.
1046 |     ...raises: [ValueError, OSError], tags: [ReadDirEffect].}
1047 |
1048 | 1049 | 1050 | 1051 |
1052 |
1053 |
1054 |
proc walk_glob(globpattern: string; prealloc: Positive = 99): seq[string] {.
1055 |     ...raises: [ValueError, OSError], tags: [ReadDirEffect].}
1056 |
1057 | 1058 | 1059 | 1060 |
1061 |
1062 |
1063 |
proc walk_simple(folderpath: string; relative: bool = false;
1064 |                  check_folders: bool = false; prealloc: Positive = 99): seq[
1065 |     string] {....raises: [OSError, ValueError], tags: [ReadDirEffect].}
1066 |
1067 | 1068 | 1069 | 1070 |
1071 |
1072 |
1073 |
func with_name(path, name: string): string {....raises: [], tags: [].}
1074 |
1075 | 1076 | 1077 | 1078 |
1079 |
1080 |
1081 |
func with_stem(path, stem: string): string {....raises: [], tags: [].}
1082 |
1083 | 1084 | 1085 | 1086 |
1087 |
1088 |
1089 |
func with_suffix(path, ext: string): string {....raises: [], tags: [].}
1090 |
1091 | 1092 | 1093 | 1094 |
1095 |
1096 |
1097 |
proc write_bytes(path, data: string): string {....raises: [IOError],
1098 |     tags: [WriteIOEffect].}
1099 |
1100 | 1101 | 1102 | 1103 |
1104 |
1105 | 1106 |
1107 | 1108 |
1109 |
1110 | 1111 |
1112 | 1117 |
1118 |
1119 |
1120 | 1121 | 1122 | 1123 | -------------------------------------------------------------------------------- /docs/nimdoc.out.css: -------------------------------------------------------------------------------- 1 | /* 2 | Stylesheet for use with Docutils/rst2html. 3 | 4 | See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to 5 | customize this style sheet. 6 | 7 | Modified from Chad Skeeters' rst2html-style 8 | https://bitbucket.org/cskeeters/rst2html-style/ 9 | 10 | Modified by Boyd Greenfield and narimiran 11 | */ 12 | 13 | :root { 14 | --primary-background: #fff; 15 | --secondary-background: ghostwhite; 16 | --third-background: #e8e8e8; 17 | --info-background: #50c050; 18 | --warning-background: #c0a000; 19 | --error-background: #e04040; 20 | --border: #dde; 21 | --text: #222; 22 | --anchor: #07b; 23 | --anchor-focus: #607c9f; 24 | --input-focus: #1fa0eb; 25 | --strong: #3c3c3c; 26 | --hint: #9A9A9A; 27 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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"); 28 | 29 | --keyword: #5e8f60; 30 | --identifier: #222; 31 | --comment: #484a86; 32 | --operator: #155da4; 33 | --punctuation: black; 34 | --other: black; 35 | --escapeSequence: #c4891b; 36 | --number: #252dbe; 37 | --literal: #a4255b; 38 | --program: #6060c0; 39 | --option: #508000; 40 | --raw-data: #a4255b; 41 | } 42 | 43 | [data-theme="dark"] { 44 | --primary-background: #171921; 45 | --secondary-background: #1e202a; 46 | --third-background: #2b2e3b; 47 | --info-background: #008000; 48 | --warning-background: #807000; 49 | --error-background: #c03000; 50 | --border: #0e1014; 51 | --text: #fff; 52 | --anchor: #8be9fd; 53 | --anchor-focus: #8be9fd; 54 | --input-focus: #8be9fd; 55 | --strong: #bd93f9; 56 | --hint: #7A7C85; 57 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); 58 | 59 | --keyword: #ff79c6; 60 | --identifier: #f8f8f2; 61 | --comment: #6272a4; 62 | --operator: #ff79c6; 63 | --punctuation: #f8f8f2; 64 | --other: #f8f8f2; 65 | --escapeSequence: #bd93f9; 66 | --number: #bd93f9; 67 | --literal: #f1fa8c; 68 | --program: #9090c0; 69 | --option: #90b010; 70 | --raw-data: #8be9fd; 71 | } 72 | 73 | .theme-switch-wrapper { 74 | display: flex; 75 | align-items: center; 76 | } 77 | 78 | .theme-switch-wrapper em { 79 | margin-left: 10px; 80 | font-size: 1rem; 81 | } 82 | 83 | .theme-switch { 84 | display: inline-block; 85 | height: 22px; 86 | position: relative; 87 | width: 50px; 88 | } 89 | 90 | .theme-switch input { 91 | display: none; 92 | } 93 | 94 | .slider { 95 | background-color: #ccc; 96 | bottom: 0; 97 | cursor: pointer; 98 | left: 0; 99 | position: absolute; 100 | right: 0; 101 | top: 0; 102 | transition: .4s; 103 | } 104 | 105 | .slider:before { 106 | background-color: #fff; 107 | bottom: 4px; 108 | content: ""; 109 | height: 13px; 110 | left: 4px; 111 | position: absolute; 112 | transition: .4s; 113 | width: 13px; 114 | } 115 | 116 | input:checked + .slider { 117 | background-color: #66bb6a; 118 | } 119 | 120 | input:checked + .slider:before { 121 | transform: translateX(26px); 122 | } 123 | 124 | .slider.round { 125 | border-radius: 17px; 126 | } 127 | 128 | .slider.round:before { 129 | border-radius: 50%; 130 | } 131 | 132 | html { 133 | font-size: 100%; 134 | -webkit-text-size-adjust: 100%; 135 | -ms-text-size-adjust: 100%; } 136 | 137 | body { 138 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 139 | font-weight: 400; 140 | font-size: 1.125em; 141 | line-height: 1.5; 142 | color: var(--text); 143 | background-color: var(--primary-background); } 144 | 145 | /* Skeleton grid */ 146 | .container { 147 | position: relative; 148 | width: 100%; 149 | max-width: 1050px; 150 | margin: 0 auto; 151 | padding: 0; 152 | box-sizing: border-box; } 153 | 154 | .column, 155 | .columns { 156 | width: 100%; 157 | float: left; 158 | box-sizing: border-box; 159 | margin-left: 1%; 160 | } 161 | 162 | .column:first-child, 163 | .columns:first-child { 164 | margin-left: 0; } 165 | 166 | .three.columns { 167 | width: 22%; 168 | } 169 | 170 | .nine.columns { 171 | width: 77.0%; } 172 | 173 | .twelve.columns { 174 | width: 100%; 175 | margin-left: 0; } 176 | 177 | @media screen and (max-width: 860px) { 178 | .three.columns { 179 | display: none; 180 | } 181 | .nine.columns { 182 | width: 98.0%; 183 | } 184 | body { 185 | font-size: 1em; 186 | line-height: 1.35; 187 | } 188 | } 189 | 190 | cite { 191 | font-style: italic !important; } 192 | 193 | 194 | /* Nim search input */ 195 | div#searchInputDiv { 196 | margin-bottom: 1em; 197 | } 198 | input#searchInput { 199 | width: 80%; 200 | } 201 | 202 | /* 203 | * Some custom formatting for input forms. 204 | * This also fixes input form colors on Firefox with a dark system theme on Linux. 205 | */ 206 | input { 207 | -moz-appearance: none; 208 | background-color: var(--secondary-background); 209 | color: var(--text); 210 | border: 1px solid var(--border); 211 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 212 | font-size: 0.9em; 213 | padding: 6px; 214 | } 215 | 216 | input:focus { 217 | border: 1px solid var(--input-focus); 218 | box-shadow: 0 0 3px var(--input-focus); 219 | } 220 | 221 | select { 222 | -moz-appearance: none; 223 | background-color: var(--secondary-background); 224 | color: var(--text); 225 | border: 1px solid var(--border); 226 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; 227 | font-size: 0.9em; 228 | padding: 6px; 229 | } 230 | 231 | select:focus { 232 | border: 1px solid var(--input-focus); 233 | box-shadow: 0 0 3px var(--input-focus); 234 | } 235 | 236 | /* Docgen styles */ 237 | 238 | :target { 239 | border: 2px solid #B5651D; 240 | border-style: dotted; 241 | } 242 | 243 | /* Links */ 244 | a { 245 | color: var(--anchor); 246 | text-decoration: none; 247 | } 248 | 249 | a span.Identifier { 250 | text-decoration: underline; 251 | text-decoration-color: #aab; 252 | } 253 | 254 | a.reference-toplevel { 255 | font-weight: bold; 256 | } 257 | 258 | a.toc-backref { 259 | text-decoration: none; 260 | color: var(--text); } 261 | 262 | a.link-seesrc { 263 | color: #607c9f; 264 | font-size: 0.9em; 265 | font-style: italic; } 266 | 267 | a:hover, 268 | a:focus { 269 | color: var(--anchor-focus); 270 | text-decoration: underline; } 271 | 272 | a:hover span.Identifier { 273 | color: var(--anchor); 274 | } 275 | 276 | 277 | sub, 278 | sup { 279 | position: relative; 280 | font-size: 75%; 281 | line-height: 0; 282 | vertical-align: baseline; } 283 | 284 | sup { 285 | top: -0.5em; } 286 | 287 | sub { 288 | bottom: -0.25em; } 289 | 290 | img { 291 | width: auto; 292 | height: auto; 293 | max-width: 100%; 294 | vertical-align: middle; 295 | border: 0; 296 | -ms-interpolation-mode: bicubic; } 297 | 298 | @media print { 299 | * { 300 | color: black !important; 301 | text-shadow: none !important; 302 | background: transparent !important; 303 | box-shadow: none !important; } 304 | 305 | a, 306 | a:visited { 307 | text-decoration: underline; } 308 | 309 | a[href]:after { 310 | content: " (" attr(href) ")"; } 311 | 312 | abbr[title]:after { 313 | content: " (" attr(title) ")"; } 314 | 315 | .ir a:after, 316 | a[href^="javascript:"]:after, 317 | a[href^="#"]:after { 318 | content: ""; } 319 | 320 | pre, 321 | blockquote { 322 | border: 1px solid #999; 323 | page-break-inside: avoid; } 324 | 325 | thead { 326 | display: table-header-group; } 327 | 328 | tr, 329 | img { 330 | page-break-inside: avoid; } 331 | 332 | img { 333 | max-width: 100% !important; } 334 | 335 | @page { 336 | margin: 0.5cm; } 337 | 338 | h1 { 339 | page-break-before: always; } 340 | 341 | h1.title { 342 | page-break-before: avoid; } 343 | 344 | p, 345 | h2, 346 | h3 { 347 | orphans: 3; 348 | widows: 3; } 349 | 350 | h2, 351 | h3 { 352 | page-break-after: avoid; } 353 | } 354 | 355 | 356 | p { 357 | margin-top: 0.5em; 358 | margin-bottom: 0.5em; 359 | } 360 | 361 | small { 362 | font-size: 85%; } 363 | 364 | strong { 365 | font-weight: 600; 366 | font-size: 0.95em; 367 | color: var(--strong); 368 | } 369 | 370 | em { 371 | font-style: italic; } 372 | 373 | h1 { 374 | font-size: 1.8em; 375 | font-weight: 400; 376 | padding-bottom: .25em; 377 | border-bottom: 6px solid var(--third-background); 378 | margin-top: 2.5em; 379 | margin-bottom: 1em; 380 | line-height: 1.2em; } 381 | 382 | h1.title { 383 | padding-bottom: 1em; 384 | border-bottom: 0px; 385 | font-size: 2.5em; 386 | text-align: center; 387 | font-weight: 900; 388 | margin-top: 0.75em; 389 | margin-bottom: 0em; 390 | } 391 | 392 | h2 { 393 | font-size: 1.3em; 394 | margin-top: 2em; } 395 | 396 | h2.subtitle { 397 | margin-top: 0em; 398 | text-align: center; } 399 | 400 | h3 { 401 | font-size: 1.125em; 402 | font-style: italic; 403 | margin-top: 1.5em; } 404 | 405 | h4 { 406 | font-size: 1.125em; 407 | margin-top: 1em; } 408 | 409 | h5 { 410 | font-size: 1.125em; 411 | margin-top: 0.75em; } 412 | 413 | h6 { 414 | font-size: 1.1em; } 415 | 416 | 417 | ul, 418 | ol { 419 | padding: 0; 420 | margin-top: 0.5em; 421 | margin-left: 0.75em; } 422 | 423 | ul ul, 424 | ul ol, 425 | ol ol, 426 | ol ul { 427 | margin-bottom: 0; 428 | margin-left: 1.25em; } 429 | 430 | ul.simple > li { 431 | list-style-type: circle; 432 | } 433 | 434 | ul.simple-boot li { 435 | list-style-type: none; 436 | margin-left: 0em; 437 | margin-bottom: 0.5em; 438 | } 439 | 440 | ol.simple > li, ul.simple > li { 441 | margin-bottom: 0.2em; 442 | margin-left: 0.4em } 443 | 444 | ul.simple.simple-toc > li { 445 | margin-top: 1em; 446 | } 447 | 448 | ul.simple-toc { 449 | list-style: none; 450 | font-size: 0.9em; 451 | margin-left: -0.3em; 452 | margin-top: 1em; } 453 | 454 | ul.simple-toc > li { 455 | list-style-type: none; 456 | } 457 | 458 | ul.simple-toc-section { 459 | list-style-type: circle; 460 | margin-left: 0.8em; 461 | color: #6c9aae; } 462 | 463 | ul.nested-toc-section { 464 | list-style-type: circle; 465 | margin-left: -0.75em; 466 | color: var(--text); 467 | } 468 | 469 | ul.nested-toc-section > li { 470 | margin-left: 1.25em; 471 | } 472 | 473 | 474 | ol.arabic { 475 | list-style: decimal; } 476 | 477 | ol.loweralpha { 478 | list-style: lower-alpha; } 479 | 480 | ol.upperalpha { 481 | list-style: upper-alpha; } 482 | 483 | ol.lowerroman { 484 | list-style: lower-roman; } 485 | 486 | ol.upperroman { 487 | list-style: upper-roman; } 488 | 489 | ul.auto-toc { 490 | list-style-type: none; } 491 | 492 | 493 | dl { 494 | margin-bottom: 1.5em; } 495 | 496 | dt { 497 | margin-bottom: -0.5em; 498 | margin-left: 0.0em; } 499 | 500 | dd { 501 | margin-left: 2.0em; 502 | margin-bottom: 3.0em; 503 | margin-top: 0.5em; } 504 | 505 | 506 | hr { 507 | margin: 2em 0; 508 | border: 0; 509 | border-top: 1px solid #aaa; } 510 | 511 | hr.footnote { 512 | width: 25%; 513 | border-top: 0.15em solid #999; 514 | margin-bottom: 0.15em; 515 | margin-top: 0.15em; 516 | } 517 | div.footnote-group { 518 | margin-left: 1em; } 519 | div.footnote-label { 520 | display: inline-block; 521 | min-width: 1.7em; 522 | } 523 | 524 | div.option-list { 525 | border: 0.1em solid var(--border); 526 | } 527 | div.option-list-item { 528 | padding-left: 12em; 529 | padding-right: 0; 530 | padding-bottom: 0.3em; 531 | padding-top: 0.3em; 532 | } 533 | div.odd { 534 | background-color: var(--secondary-background); 535 | } 536 | div.option-list-label { 537 | margin-left: -11.5em; 538 | margin-right: 0em; 539 | min-width: 11.5em; 540 | display: inline-block; 541 | vertical-align: top; 542 | } 543 | div.option-list-description { 544 | width: calc(100% - 1em); 545 | padding-left: 1em; 546 | padding-right: 0; 547 | display: inline-block; 548 | } 549 | 550 | blockquote { 551 | font-size: 0.9em; 552 | font-style: italic; 553 | padding-left: 0.5em; 554 | margin-left: 0; 555 | border-left: 5px solid #bbc; 556 | } 557 | 558 | .pre, span.tok { 559 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 560 | font-weight: 500; 561 | font-size: 0.85em; 562 | color: var(--text); 563 | background-color: var(--third-background); 564 | padding-left: 3px; 565 | padding-right: 3px; 566 | border-radius: 4px; 567 | } 568 | 569 | span.tok { 570 | border: 1px solid #808080; 571 | padding-bottom: 0.1em; 572 | margin-right: 0.2em; 573 | } 574 | 575 | pre { 576 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 577 | color: var(--text); 578 | font-weight: 500; 579 | display: inline-block; 580 | box-sizing: border-box; 581 | min-width: 100%; 582 | padding: 0.5em; 583 | margin-top: 0.5em; 584 | margin-bottom: 0.5em; 585 | font-size: 0.85em; 586 | white-space: pre !important; 587 | overflow-y: hidden; 588 | overflow-x: visible; 589 | background-color: var(--secondary-background); 590 | border: 1px solid var(--border); 591 | -webkit-border-radius: 6px; 592 | -moz-border-radius: 6px; 593 | border-radius: 6px; } 594 | 595 | .pre-scrollable { 596 | max-height: 340px; 597 | overflow-y: scroll; } 598 | 599 | 600 | /* Nim line-numbered tables */ 601 | .line-nums-table { 602 | width: 100%; 603 | table-layout: fixed; } 604 | 605 | table.line-nums-table { 606 | border-radius: 4px; 607 | border: 1px solid #cccccc; 608 | background-color: ghostwhite; 609 | border-collapse: separate; 610 | margin-top: 15px; 611 | margin-bottom: 25px; } 612 | 613 | .line-nums-table tbody { 614 | border: none; } 615 | 616 | .line-nums-table td pre { 617 | border: none; 618 | background-color: transparent; } 619 | 620 | .line-nums-table td.blob-line-nums { 621 | width: 28px; } 622 | 623 | .line-nums-table td.blob-line-nums pre { 624 | color: #b0b0b0; 625 | -webkit-filter: opacity(75%); 626 | filter: opacity(75%); 627 | text-align: right; 628 | border-color: transparent; 629 | background-color: transparent; 630 | padding-left: 0px; 631 | margin-left: 0px; 632 | padding-right: 0px; 633 | margin-right: 0px; } 634 | 635 | 636 | table { 637 | max-width: 100%; 638 | background-color: transparent; 639 | margin-top: 0.5em; 640 | margin-bottom: 1.5em; 641 | border-collapse: collapse; 642 | border-color: var(--third-background); 643 | border-spacing: 0; 644 | font-size: 0.9em; 645 | } 646 | 647 | table th, table td { 648 | padding: 0px 0.5em 0px; 649 | border-color: var(--third-background); 650 | } 651 | 652 | table th { 653 | background-color: var(--third-background); 654 | border-color: var(--third-background); 655 | font-weight: bold; } 656 | 657 | table th.docinfo-name { 658 | background-color: transparent; 659 | text-align: right; 660 | } 661 | 662 | table tr:hover { 663 | background-color: var(--third-background); } 664 | 665 | 666 | /* rst2html default used to remove borders from tables and images */ 667 | .borderless, table.borderless td, table.borderless th { 668 | border: 0; } 669 | 670 | table.borderless td, table.borderless th { 671 | /* Override padding for "table.docutils td" with "! important". 672 | The right padding separates the table cells. */ 673 | padding: 0 0.5em 0 0 !important; } 674 | 675 | .admonition { 676 | padding: 0.3em; 677 | background-color: var(--secondary-background); 678 | border-left: 0.4em solid #7f7f84; 679 | margin-bottom: 0.5em; 680 | -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 681 | -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 682 | box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); 683 | } 684 | .admonition-info { 685 | border-color: var(--info-background); 686 | } 687 | .admonition-info-text { 688 | color: var(--info-background); 689 | } 690 | .admonition-warning { 691 | border-color: var(--warning-background); 692 | } 693 | .admonition-warning-text { 694 | color: var(--warning-background); 695 | } 696 | .admonition-error { 697 | border-color: var(--error-background); 698 | } 699 | .admonition-error-text { 700 | color: var(--error-background); 701 | } 702 | 703 | .first { 704 | /* Override more specific margin styles with "! important". */ 705 | margin-top: 0 !important; } 706 | 707 | .last, .with-subtitle { 708 | margin-bottom: 0 !important; } 709 | 710 | .hidden { 711 | display: none; } 712 | 713 | blockquote.epigraph { 714 | margin: 2em 5em; } 715 | 716 | dl.docutils dd { 717 | margin-bottom: 0.5em; } 718 | 719 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { 720 | overflow: hidden; } 721 | 722 | 723 | div.figure { 724 | margin-left: 2em; 725 | margin-right: 2em; } 726 | 727 | div.footer, div.header { 728 | clear: both; 729 | text-align: center; 730 | color: #666; 731 | font-size: smaller; } 732 | 733 | div.footer { 734 | padding-top: 5em; 735 | } 736 | 737 | div.line-block { 738 | display: block; 739 | margin-top: 1em; 740 | margin-bottom: 1em; } 741 | 742 | div.line-block div.line-block { 743 | margin-top: 0; 744 | margin-bottom: 0; 745 | margin-left: 1.5em; } 746 | 747 | div.topic { 748 | margin: 2em; } 749 | 750 | div.search_results { 751 | background-color: var(--third-background); 752 | margin: 3em; 753 | padding: 1em; 754 | border: 1px solid #4d4d4d; 755 | } 756 | 757 | div#global-links ul { 758 | margin-left: 0; 759 | list-style-type: none; 760 | } 761 | 762 | div#global-links > simple-boot { 763 | margin-left: 3em; 764 | } 765 | 766 | hr.docutils { 767 | width: 75%; } 768 | 769 | img.align-left, .figure.align-left, object.align-left { 770 | clear: left; 771 | float: left; 772 | margin-right: 1em; } 773 | 774 | img.align-right, .figure.align-right, object.align-right { 775 | clear: right; 776 | float: right; 777 | margin-left: 1em; } 778 | 779 | img.align-center, .figure.align-center, object.align-center { 780 | display: block; 781 | margin-left: auto; 782 | margin-right: auto; } 783 | 784 | .align-left { 785 | text-align: left; } 786 | 787 | .align-center { 788 | clear: both; 789 | text-align: center; } 790 | 791 | .align-right { 792 | text-align: right; } 793 | 794 | /* reset inner alignment in figures */ 795 | div.align-right { 796 | text-align: inherit; } 797 | 798 | p.attribution { 799 | text-align: right; 800 | margin-left: 50%; } 801 | 802 | p.caption { 803 | font-style: italic; } 804 | 805 | p.credits { 806 | font-style: italic; 807 | font-size: smaller; } 808 | 809 | p.label { 810 | white-space: nowrap; } 811 | 812 | p.rubric { 813 | font-weight: bold; 814 | font-size: larger; 815 | color: maroon; 816 | text-align: center; } 817 | 818 | p.topic-title { 819 | font-weight: bold; } 820 | 821 | pre.address { 822 | margin-bottom: 0; 823 | margin-top: 0; 824 | font: inherit; } 825 | 826 | pre.literal-block, pre.doctest-block, pre.math, pre.code { 827 | margin-left: 2em; 828 | margin-right: 2em; } 829 | 830 | pre.code .ln { 831 | color: grey; } 832 | 833 | /* line numbers */ 834 | pre.code, code { 835 | background-color: #eeeeee; } 836 | 837 | pre.code .comment, code .comment { 838 | color: #5c6576; } 839 | 840 | pre.code .keyword, code .keyword { 841 | color: #3B0D06; 842 | font-weight: bold; } 843 | 844 | pre.code .literal.string, code .literal.string { 845 | color: #0c5404; } 846 | 847 | pre.code .name.builtin, code .name.builtin { 848 | color: #352b84; } 849 | 850 | pre.code .deleted, code .deleted { 851 | background-color: #DEB0A1; } 852 | 853 | pre.code .inserted, code .inserted { 854 | background-color: #A3D289; } 855 | 856 | span.classifier { 857 | font-style: oblique; } 858 | 859 | span.classifier-delimiter { 860 | font-weight: bold; } 861 | 862 | span.problematic { 863 | color: #b30000; } 864 | 865 | span.section-subtitle { 866 | /* font-size relative to parent (h1..h6 element) */ 867 | font-size: 80%; } 868 | 869 | span.DecNumber { 870 | color: var(--number); } 871 | 872 | span.BinNumber { 873 | color: var(--number); } 874 | 875 | span.HexNumber { 876 | color: var(--number); } 877 | 878 | span.OctNumber { 879 | color: var(--number); } 880 | 881 | span.FloatNumber { 882 | color: var(--number); } 883 | 884 | span.Identifier { 885 | color: var(--identifier); } 886 | 887 | span.Keyword { 888 | font-weight: 600; 889 | color: var(--keyword); } 890 | 891 | span.StringLit { 892 | color: var(--literal); } 893 | 894 | span.LongStringLit { 895 | color: var(--literal); } 896 | 897 | span.CharLit { 898 | color: var(--literal); } 899 | 900 | span.EscapeSequence { 901 | color: var(--escapeSequence); } 902 | 903 | span.Operator { 904 | color: var(--operator); } 905 | 906 | span.Punctuation { 907 | color: var(--punctuation); } 908 | 909 | span.Comment, span.LongComment { 910 | font-style: italic; 911 | font-weight: 400; 912 | color: var(--comment); } 913 | 914 | span.RegularExpression { 915 | color: darkviolet; } 916 | 917 | span.TagStart { 918 | color: darkviolet; } 919 | 920 | span.TagEnd { 921 | color: darkviolet; } 922 | 923 | span.Key { 924 | color: #252dbe; } 925 | 926 | span.Value { 927 | color: #252dbe; } 928 | 929 | span.RawData { 930 | color: var(--raw-data); } 931 | 932 | span.Assembler { 933 | color: #252dbe; } 934 | 935 | span.Preprocessor { 936 | color: #252dbe; } 937 | 938 | span.Directive { 939 | color: #252dbe; } 940 | 941 | span.option { 942 | font-weight: bold; 943 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; 944 | color: var(--option); 945 | } 946 | 947 | span.Prompt { 948 | font-weight: bold; 949 | color: red; } 950 | 951 | span.ProgramOutput { 952 | font-weight: bold; 953 | color: #808080; } 954 | 955 | span.program { 956 | font-weight: bold; 957 | color: var(--program); 958 | text-decoration: underline; 959 | text-decoration-color: var(--hint); 960 | text-decoration-thickness: 0.05em; 961 | text-underline-offset: 0.15em; 962 | } 963 | 964 | span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, 965 | span.Other { 966 | color: var(--other); } 967 | 968 | /* Pop type, const, proc, and iterator defs in nim def blocks */ 969 | dt pre > span.Identifier, dt pre > span.Operator { 970 | color: var(--identifier); 971 | font-weight: 700; } 972 | 973 | dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, 974 | dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { 975 | color: var(--identifier); 976 | font-weight: inherit; } 977 | 978 | /* Nim sprite for the footer (taken from main page favicon) */ 979 | .nim-sprite { 980 | display: inline-block; 981 | width: 51px; 982 | height: 14px; 983 | background-position: 0 0; 984 | background-size: 51px 14px; 985 | -webkit-filter: opacity(50%); 986 | filter: opacity(50%); 987 | background-repeat: no-repeat; 988 | background-image: var(--nim-sprite-base64); 989 | margin-bottom: 5px; } 990 | 991 | span.pragmadots { 992 | /* Position: relative frees us up to make the dots 993 | look really nice without fucking up the layout and 994 | causing bulging in the parent container */ 995 | position: relative; 996 | /* 1px down looks slightly nicer */ 997 | top: 1px; 998 | padding: 2px; 999 | background-color: var(--third-background); 1000 | border-radius: 4px; 1001 | margin: 0 2px; 1002 | cursor: pointer; 1003 | font-size: 0.8em; 1004 | } 1005 | 1006 | span.pragmadots:hover { 1007 | background-color: var(--hint); 1008 | } 1009 | span.pragmawrap { 1010 | display: none; 1011 | } 1012 | 1013 | span.attachedType { 1014 | display: none; 1015 | visibility: hidden; 1016 | } 1017 | -------------------------------------------------------------------------------- /examples/.env: -------------------------------------------------------------------------------- 1 | # This is a comment = 42 2 | json0 = {"key":"value"} # json 3 | k="v" # string 4 | x = 42 # int 5 | v = 2.0 # float 6 | o = true # bool 7 | b = false # bool 8 | t = +inf # float 9 | w = -inf # float 10 | g = NaN # float 11 | cc="" # string 12 | x="" # int 13 | t = 9999999999999999999 # int 14 | isConsoleApplication=False # bool 15 | COMPILE=True # bool 16 | BASIC=basic # string 17 | 18 | # previous line intentionally left blank 19 | AFTER_LINE=after_line # string 20 | EMPTY= # string 21 | SINGLE_QUOTES='single_quotes' # string 22 | SINGLE_QUOTES_SPACED=' single quotes ' # string 23 | DOUBLE_QUOTES="double_quotes" # string 24 | DOUBLE_QUOTES_SPACED=" double quotes " # string 25 | RETAIN_INNER_QUOTES={"foo": "bar"} # json 26 | RETAIN_LEADING_DQUOTE="retained # string 27 | RETAIN_LEADING_SQUOTE='retained # string 28 | RETAIN_TRAILING_DQUOTE=retained" # string 29 | RETAIN_TRAILING_SQUOTE=retained' # string 30 | RETAIN_INNER_QUOTES_AS_STRING='{"k":"v"}' # string 31 | TRIM_SPACE_FROM_UNQUOTED= spaced out string # string 32 | USERNAME=therealnerdybeast@example.tld # string 33 | SPACED_KEY = parsed # string 34 | EXPAND_NEWLINES="expand\nnew\nlines" # string 35 | DONT_EXPAND_UNQUOTED=dontexpand\nnewlines # string 36 | DONT_EXPAND_SQUOTED='dontexpand\nnewlines' # string 37 | EQUAL_SIGNS=equals== # string 38 | -------------------------------------------------------------------------------- /examples/example_chmod.py: -------------------------------------------------------------------------------- 1 | from thatlib import chmod 2 | chmod(__file__, 0o777) 3 | -------------------------------------------------------------------------------- /examples/example_cwd.py: -------------------------------------------------------------------------------- 1 | from thatlib import cwd 2 | print(cwd()) 3 | -------------------------------------------------------------------------------- /examples/example_dotenv.py: -------------------------------------------------------------------------------- 1 | from thatlib import dotenv 2 | print(dotenv(".env")) # ".env" file assumed to be in the same folder for this example. 3 | -------------------------------------------------------------------------------- /examples/example_expanduser.py: -------------------------------------------------------------------------------- 1 | from thatlib import expanduser 2 | print(expanduser("~/")) 3 | -------------------------------------------------------------------------------- /examples/example_get_exe.py: -------------------------------------------------------------------------------- 1 | from thatlib import get_exe 2 | print(get_exe("python")) 3 | -------------------------------------------------------------------------------- /examples/example_get_file_info.py: -------------------------------------------------------------------------------- 1 | from thatlib import get_file_info 2 | print(get_file_info(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_get_file_times.py: -------------------------------------------------------------------------------- 1 | from thatlib import get_file_times_iso 2 | print(get_file_times_iso(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_get_size.py: -------------------------------------------------------------------------------- 1 | from thatlib import get_size 2 | print(get_size(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_home.py: -------------------------------------------------------------------------------- 1 | from thatlib import home 2 | print(home()) 3 | -------------------------------------------------------------------------------- /examples/example_is_file.py: -------------------------------------------------------------------------------- 1 | from thatlib import is_file 2 | print(is_file(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_is_valid_path.py: -------------------------------------------------------------------------------- 1 | from thatlib import is_valid_path 2 | print(is_valid_path("NUL")) 3 | print(is_valid_path("LPT1")) 4 | print(is_valid_path("COM1")) 5 | print(is_valid_path("CON")) 6 | -------------------------------------------------------------------------------- /examples/example_joinpath.py: -------------------------------------------------------------------------------- 1 | from thatlib import joinpath 2 | print(joinpath(["foo", "bar", "baz"])) 3 | -------------------------------------------------------------------------------- /examples/example_lines.py: -------------------------------------------------------------------------------- 1 | from thatlib import lines 2 | print(lines(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_mkdir.py: -------------------------------------------------------------------------------- 1 | from thatlib import mkdir 2 | mkdir("example") 3 | -------------------------------------------------------------------------------- /examples/example_path_splitted.py: -------------------------------------------------------------------------------- 1 | from thatlib import path_splitted 2 | print(path_splitted(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_replaced.py: -------------------------------------------------------------------------------- 1 | from thatlib import replaced 2 | print(replaced(__file__, [("example", "sample")])) 3 | -------------------------------------------------------------------------------- /examples/example_resolve.py: -------------------------------------------------------------------------------- 1 | from thatlib import resolve 2 | print(resolve("./")) 3 | -------------------------------------------------------------------------------- /examples/example_rmdir.py: -------------------------------------------------------------------------------- 1 | from thatlib import rmdir 2 | rmdir("example") 3 | -------------------------------------------------------------------------------- /examples/example_tokenized.py: -------------------------------------------------------------------------------- 1 | from thatlib import tokenized 2 | print(tokenized(__file__)) 3 | -------------------------------------------------------------------------------- /examples/example_walk.py: -------------------------------------------------------------------------------- 1 | from thatlib import walk 2 | print(walk( 3 | folderpath = ".", 4 | extensions = [".py", ".nim"], 5 | followlinks = False, 6 | yieldfiles = True, 7 | debugs = True, 8 | check_folders = False, 9 | prealloc = 9, 10 | )) 11 | -------------------------------------------------------------------------------- /examples/example_walk_glob.py: -------------------------------------------------------------------------------- 1 | from thatlib import walk_glob 2 | print(walk_glob("*.py")) 3 | -------------------------------------------------------------------------------- /examples/example_with_name.py: -------------------------------------------------------------------------------- 1 | from thatlib import with_name 2 | print(with_name(__file__, "temp.txt")) 3 | -------------------------------------------------------------------------------- /examples/example_with_stem.py: -------------------------------------------------------------------------------- 1 | from thatlib import with_stem 2 | print(with_stem(__file__, "OwO")) 3 | -------------------------------------------------------------------------------- /examples/example_with_suffix.py: -------------------------------------------------------------------------------- 1 | from thatlib import with_suffix 2 | print(with_suffix(__file__, ".txt")) 3 | -------------------------------------------------------------------------------- /package4pypi.sh: -------------------------------------------------------------------------------- 1 | rm --verbose --force dist/*.zip 2 | rm --verbose --force --recursive dist/thatlib/ 3 | rm --verbose --force --recursive dist/thatlib/__pycache__/ 4 | cp --verbose --recursive thatlib dist/ 5 | cd dist && zip -9 -T -v -r thatlib.zip * 6 | -------------------------------------------------------------------------------- /results_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juancarlospaco/thatlib/75acf89e7bd0fff6583238fd2481aa97f5877fae/results_graph.png -------------------------------------------------------------------------------- /run-benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 benchmark.py # Start script for Benchmark. 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | from nimporter import get_nim_extensions, WINDOWS, MACOS, LINUX 3 | 4 | setuptools.setup( 5 | name = 'thatlib', 6 | install_requires = ['nimporter'], 7 | py_modules = ['thatlib.py'], 8 | ext_modules = get_nim_extensions(platforms = (WINDOWS, LINUX, MACOS)), 9 | ) 10 | -------------------------------------------------------------------------------- /thatlib-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juancarlospaco/thatlib/75acf89e7bd0fff6583238fd2481aa97f5877fae/thatlib-logo.jpg -------------------------------------------------------------------------------- /thatlib/thatlib.nim: -------------------------------------------------------------------------------- 1 | ## .. image:: https://raw.githubusercontent.com/juancarlospaco/thatlib/nim/results_graph.png 2 | import std/[strutils, os, sha1, md5, json, parseutils, times], nimpy 3 | 4 | proc cwd*(): string {.exportpy.} = 5 | getCurrentDir() 6 | 7 | proc home*(): string {.exportpy.} = 8 | getHomeDir() 9 | 10 | proc mkdir*(path: string) {.exportpy.} = 11 | createDir(path) 12 | 13 | proc rmdir*(path: string; check: bool = false) {.exportpy.} = 14 | removeDir(path, check) 15 | 16 | proc is_file*(path: string): bool {.exportpy.} = 17 | fileExists(path) 18 | 19 | proc is_dir*(path: string): bool {.exportpy.} = 20 | dirExists(path) 21 | 22 | proc exists*(path: string): bool {.exportpy.} = 23 | fileExists(path) or dirExists(path) 24 | 25 | proc rename*(source, destination: string) {.exportpy.} = 26 | moveFile(source, destination) 27 | 28 | proc replace*(source, destination: string): string {.exportpy.} = 29 | moveFile(source, destination) 30 | result = destination 31 | 32 | proc resolve*(path: string): string {.exportpy.} = 33 | absolutePath(path) 34 | 35 | proc absolute*(path: string): string {.exportpy.} = 36 | absolutePath(path) 37 | 38 | func samefile*(pathA, pathB: string): bool {.exportpy.} = 39 | sameFile(pathA, pathB) 40 | 41 | func is_absolute*(path: string): bool {.exportpy.} = 42 | os.isAbsolute(path) 43 | 44 | func joinpath*(paths: openArray[string]): string {.exportpy.} = 45 | os.joinPath(paths) 46 | 47 | func with_suffix*(path, ext: string): string {.exportpy.} = 48 | changeFileExt(path, ext) 49 | 50 | func with_stem*(path, stem: string): string {.exportpy.} = 51 | let f = splitFile(path) 52 | result = f.dir / stem & f.ext 53 | 54 | func with_name*(path, name: string): string {.exportpy.} = 55 | let f = splitFile(path) 56 | result = f.dir / name 57 | 58 | func suffix*(path: string): string {.exportpy.} = 59 | splitFile(path).ext 60 | 61 | func stem*(path: string): string {.exportpy.} = 62 | splitFile(path).name 63 | 64 | func parent*(path: string): string {.exportpy.} = 65 | splitFile(path).dir 66 | 67 | proc write_bytes*(path, data: string): string {.exportpy.} = 68 | writeFile(path, data) 69 | 70 | proc read_bytes*(path: string): string {.exportpy.} = 71 | readFile(path) 72 | 73 | proc is_relative_to*(path, base: string): bool {.exportpy.} = 74 | os.isRelativeTo(path, base) 75 | 76 | proc expanduser*(path: string): string {.exportpy.} = 77 | os.expandTilde(path) 78 | 79 | proc as_uri*(path: string): string {.exportpy.} = 80 | (when defined(windows): "file:///" else: "file://") / absolutePath(path) 81 | 82 | proc parts*(path: string): seq[string] {.exportpy.} = 83 | absolutePath(path).split(DirSep) 84 | 85 | proc chmod*(path: string; permissions: uint) {.exportpy.} = 86 | var results: set[FilePermission] 87 | var perm = permissions 88 | for permBase in [fpOthersExec, fpGroupExec, fpUserExec]: 89 | if (perm and 1) != 0: results.incl permBase # Exec 90 | if (perm and 2) != 0: results.incl permBase.succ() # Read 91 | if (perm and 4) != 0: results.incl permBase.succ(2) # Write 92 | perm = perm shr 3 # Shift to next permission group 93 | setFilePermissions(path, results) 94 | 95 | 96 | # ^ API mimic from Python "pathlib" ################################## v Extras 97 | 98 | 99 | proc is_root*(): bool {.exportpy.} = 100 | when compiles(os.isAdmin): os.isAdmin() 101 | 102 | proc symlink*(source, destination: string) {.exportpy.} = 103 | os.createSymlink(source, destination) 104 | 105 | proc hardlink*(source, destination: string) {.exportpy.} = 106 | os.createHardlink(source, destination) 107 | 108 | proc env_vars_pairs*(): seq[(string, string)] {.exportpy.} = 109 | for item in os.envPairs(): result.add item 110 | 111 | func parents*(path: string): string {.exportpy.} = 112 | parentDir(path) 113 | 114 | func is_fs_casesensitive*(): bool {.exportpy.} = 115 | os.FileSystemCaseSensitive 116 | 117 | func get_dynlib_format*(): string {.exportpy.} = 118 | os.DynlibFormat 119 | 120 | proc lines*(path: string; start: int = 0; ends: int = 1): seq[string] {.exportpy.} = 121 | readFile(path).splitLines[start .. ^ends] 122 | 123 | proc line*(path: string; line_number: Natural): string {.exportpy.} = 124 | readFile(path).splitLines[line_number] 125 | 126 | proc counted_lines*(path: string): int {.exportpy.} = 127 | readFile(path).countLines 128 | 129 | proc tokenized*(path: string): seq[tuple[token: string, isSep: bool]] {.exportpy.} = 130 | for item in tokenize(readFile(path)): result.add item 131 | 132 | func replaced*(path: string; replacements: openArray[(string, string)]): string {.exportpy.} = 133 | path.multiReplace(replacements) 134 | 135 | func normalized*(path: string): string {.exportpy.} = 136 | path.strip.normalize 137 | 138 | func is_root_dir*(path: string): bool {.exportpy.} = 139 | isRootDir(path) 140 | 141 | func get_suffix_index*(path: string): int {.exportpy.} = 142 | searchExtPos(path) 143 | 144 | func path_splitted*(path: string): tuple[dir, name, ext: string] {.exportpy.} = 145 | splitFile(path) 146 | 147 | func compare_paths*(pathA, pathB: string): int {.exportpy.} = 148 | cmpPaths(pathA, pathB) 149 | 150 | proc get_conf_dir*(): string {.exportpy.} = 151 | getConfigDir() 152 | 153 | proc get_temporary_dir*(): string {.exportpy.} = 154 | getTempDir() 155 | 156 | func paths_quoted*(paths: openArray[string]): string {.exportpy.} = 157 | quoteShellCommand(paths) 158 | 159 | proc is_file_newer*(pathA, pathB: string): bool {.exportpy.} = 160 | fileNewer(pathA, pathB) 161 | 162 | func symlink_exists*(path: string): bool {.exportpy.} = 163 | symlinkExists(path) 164 | 165 | proc get_exe*(path: string; followSymlinks: bool = true): string {.exportpy.} = 166 | findExe(path, followSymlinks) 167 | 168 | proc mksymlink*(source, destination: string): uint {.exportpy.} = 169 | createSymlink(source, destination) 170 | 171 | proc get_symlink*(path: string): string {.exportpy.} = 172 | expandSymlink(path) 173 | 174 | proc try_rmfile*(path: string): bool {.exportpy.} = 175 | tryRemoveFile(path) 176 | 177 | proc exists_create_dir*(path: string): bool {.exportpy.} = 178 | existsOrCreateDir(path) 179 | 180 | proc mkhardlink*(source, destination: string) {.exportpy.} = 181 | createHardlink(source, destination) 182 | 183 | proc get_size*(path: string): BiggestInt {.exportpy.} = 184 | getFileSize(path) 185 | 186 | template divmod(a, b): array[2, int] = 187 | [int(int(a) / int(b)), int(a mod b)] 188 | 189 | template abytes(stringy: var string; unit: static[string]) = 190 | when len(unit) > 0: 191 | stringy.add unit # "SOMETHINGbytes" 192 | stringy.add 'b' 193 | else: 194 | stringy.add 'B' # Just "Bytes" 195 | stringy.add 'y' 196 | stringy.add 't' 197 | stringy.add 'e' 198 | stringy.add 's' 199 | stringy.add ' ' 200 | 201 | template bytes2human(integer_bytes: int64; result: var string) = 202 | var bite, kilo, mega, giga, tera, peta, exa, zetta, yotta: int64 203 | (kilo, bite) = divmod(integer_bytes, int64(1_024)) 204 | (mega, kilo) = divmod(kilo, int64(1_024)) 205 | (giga, mega) = divmod(mega, int64(1_024)) 206 | (tera, giga) = divmod(giga, int64(1_024)) 207 | (peta, tera) = divmod(tera, int64(1_024)) 208 | (exa, peta) = divmod(peta, int64(1_024)) 209 | (zetta, exa) = divmod(exa, int64(1_024)) 210 | (yotta, zetta) = divmod(zetta, int64(1_024)) 211 | if unlikely(zetta > 0): 212 | result.addInt zetta 213 | abytes(result, "Zetta") 214 | if unlikely(exa > 0): 215 | result.addInt exa 216 | abytes(result, "Exa") 217 | if unlikely(peta > 0): 218 | result.addInt peta 219 | abytes(result, "Peta") 220 | if tera > 0: 221 | result.addInt tera 222 | abytes(result, "Tera") 223 | if giga > 0: 224 | result.addInt giga 225 | abytes(result, "Giga") 226 | if mega > 0: 227 | result.addInt mega 228 | abytes(result, "Mega") 229 | if kilo > 0: 230 | result.addInt kilo 231 | abytes(result, "Kilo") 232 | result.addInt bite 233 | abytes(result, "") 234 | 235 | proc get_size_human*(path: string): string {.exportpy.} = 236 | bytes2human(getFileSize(path), result) 237 | 238 | proc is_hidden_path*(path: string) : bool {.exportpy.} = 239 | isHidden(path) 240 | 241 | func is_valid_path*(path: string) : bool {.exportpy.} = 242 | isValidFilename(path) 243 | 244 | proc get_md5*(path: string): string {.exportpy.} = 245 | md5.getMD5(readFile(path)) 246 | 247 | proc get_sha1*(path: string): string {.exportpy.} = 248 | $secureHashFile(path) 249 | 250 | proc copy_file_permissions*(source, dest: string; ignore_errors: bool = true) {.exportpy.} = 251 | copyFileWithPermissions(source, dest, ignore_errors) 252 | 253 | proc copy_dir_permissions*(source, dest: string; ignore_errors: bool = true) {.exportpy.} = 254 | copyDirWithPermissions(source, dest, ignore_errors) 255 | 256 | func fromFilePermissions(perm: set[FilePermission]): uint {.inline.} = 257 | if fpUserExec in perm: inc result, 0o100 # User 258 | if fpUserWrite in perm: inc result, 0o200 259 | if fpUserRead in perm: inc result, 0o400 260 | if fpGroupExec in perm: inc result, 0o010 # Group 261 | if fpGroupWrite in perm: inc result, 0o020 262 | if fpGroupRead in perm: inc result, 0o040 263 | if fpOthersExec in perm: inc result, 0o001 # Others 264 | if fpOthersWrite in perm: inc result, 0o002 265 | if fpOthersRead in perm: inc result, 0o004 266 | 267 | proc get_file_info*(path: string; follow_symlinks: bool = true): tuple[size, link_count, block_size, permissions: int64] {.exportpy.} = 268 | let f = os.getFileInfo(path, follow_symlinks) 269 | result = ( 270 | size: int64(f.size), link_count: int64(f.linkCount - 1), 271 | block_size: int64(when compiles(f.blockSize): f.blockSize else: 0), 272 | permissions: int64(fromFilePermissions(f.permissions)), # Permissions are Octal Unix. 273 | ) 274 | 275 | proc get_file_times_iso*(path: string; follow_symlinks: bool = true): tuple[last_access, last_write, creation: string] {.exportpy.} = 276 | let f = os.getFileInfo(path, follow_symlinks) 277 | result = (last_access: $f.lastAccessTime, last_write: $f.lastWriteTime, creation: $f.creationTime) 278 | 279 | proc get_file_times_unix*(path: string; follow_symlinks: bool = true): tuple[last_access, last_write, creation: int64] {.exportpy.} = 280 | let f = os.getFileInfo(path, follow_symlinks) 281 | result = (last_access: toUnix(f.lastAccessTime), last_write: toUnix(f.lastWriteTime), creation: toUnix(f.creationTime)) 282 | 283 | proc walk*(folderpath: string; extensions: seq[string] = @[""]; followlinks : bool = false; yieldfiles: bool = true; debugs: bool = false; check_folders: bool = false; prealloc: Positive = 99): seq[string] {.exportpy.} = 284 | result = newSeqOfCap[string](prealloc) 285 | let extused {.noalias.} = create bool 286 | extused[] = extensions != @[""] and extensions.len > 0 287 | for item in walkDirRec(folderpath, {if yieldfiles: pcFile else: pcDir}, {if followlinks: pcLinkToDir else: pcDir}, checkDir = check_folders): 288 | if unlikely(debugs): echo item 289 | if unlikely(extused[]): 290 | for ext in extensions: 291 | if item.normalize.endsWith(ext): result.add absolutePath(item) 292 | else: result.add absolutePath(item) 293 | if extused != nil: dealloc extused 294 | 295 | proc walk_glob*(globpattern: string; prealloc: Positive = 99): seq[string] {.exportpy.} = 296 | result = newSeqOfCap[string](prealloc) 297 | for item in walkPattern(globpattern): result.add absolutePath(item) 298 | 299 | proc walk_simple*(folderpath: string; relative: bool = false; check_folders: bool = false; prealloc: Positive = 99): seq[string] {.exportpy.} = 300 | result = newSeqOfCap[string](prealloc) 301 | for item in walkDirRec(folderpath, relative=relative, checkDir=check_folders): result.add absolutePath(item) 302 | 303 | proc walk_folders*(globpattern: string; prealloc: Positive = 99): seq[string] {.exportpy.} = 304 | result = newSeqOfCap[string](prealloc) 305 | for item in walkDirs(globpattern): result.add absolutePath(item) 306 | 307 | proc walk_files*(globpattern: string; prealloc: Positive = 99): seq[string] {.exportpy.} = 308 | result = newSeqOfCap[string](prealloc) 309 | for item in walkFiles(globpattern): result.add absolutePath(item) 310 | 311 | proc parseBool(s: string): bool {.inline.} = 312 | case s 313 | of "y", "Y", "1", "ON", "On", "oN", "on", 314 | "yes", "YES", "YEs", "YeS", "Yes", "yES", "yEs", "yeS", 315 | "TRUE", "TRUe", "TRuE", "TRue", "TrUE", "TrUe", "TruE", "True", "tRUE", 316 | "tRUe", "tRuE", "tRue", "trUE", "trUe", "truE", "true": result = true 317 | of "n", "N", "0", "NO", "No", "nO", "no", "", 318 | "OFF", "OFf", "OfF", "Off", "oFF", "oFf", "ofF", "off", 319 | "FALSE", "FALSe", "FALsE", "FALse", "FAlSE", "FAlSe", "FAlsE", "FAlse", 320 | "FaLSE", "FaLSe", "FaLsE", "FaLse", "FalSE", "FalSe", "FalsE", "False", 321 | "fALSE", "fALSe", "fALsE", "fALse", "fAlSE", "fAlSe", "fAlsE", "fAlse", 322 | "faLSE", "faLSe", "faLsE", "faLse", "falSE", "falSe", "falsE", "false": result = false 323 | else: doAssert false, "cannot interpret as a bool" 324 | 325 | func strip(s: var string) = 326 | var first = 0 327 | var last = s.high 328 | while first <= last and s[first] in {' ', '\t'}: inc first 329 | while last >= first and s[last] in {' ', '\t'}: dec last 330 | if unlikely(first > last): 331 | s.setLen 0 332 | return 333 | template impl = 334 | for index in first .. last: s[index - first] = s[index] 335 | if first > 0: 336 | when nimvm: impl() 337 | else: 338 | when not declared(moveMem): impl() 339 | else: 340 | when defined(nimSeqsV2) and declared(prepareMutation): prepareMutation(s) 341 | moveMem(addr s[0], addr s[first], last - first + 1) 342 | s.setLen last - first + 1 343 | 344 | func validateKey(s: string): bool {.inline.} = 345 | result = true 346 | for c in s: 347 | if c notin {'a'..'z', 'A'..'Z', '0'..'9', '_'}: return false 348 | 349 | proc dotenv*(path: string): string {.exportpy.} = 350 | var s = readFile(path) 351 | var temp = newJObject() 352 | if likely(s.len > 1): 353 | for zz in s.split('\n'): # Split by lines 354 | var z = zz # k= is the shortest possible 355 | strip(z) 356 | if z.len > 1 and z[0] != '#': # No comment lines, no empty lines 357 | let k_v = z.split('=') 358 | if k_v.len >= 2: # k sep v 359 | var k = k_v[0] # Key name 360 | strip(k) 361 | doAssert validateKey(k), "DotEnv key must be a non-empty ASCII string ([a-zA-Z0-9_])" 362 | var v = k_v[1].split('#')[0] # remove inline comments 363 | strip(v) 364 | var tipe = k_v[^1].split('#')[1] # Get type annotation 365 | strip(tipe) 366 | if k.len > 0: # k must not be empty string 367 | case tipe 368 | of "bool": temp.add k, newJBool(parseBool(v)) 369 | of "string": temp.add k, newJString(v) 370 | of "json": temp.add k, parseJson(v) 371 | of "int": 372 | var i = 0 373 | discard parseSaturatedNatural(v, i) 374 | temp.add k, newJInt(i) 375 | of "float": 376 | var f = 0.0 377 | discard parseFloat(v, f) 378 | temp.add k, newJFloat(f) 379 | else: doAssert false, "Type must be 1 of int, float, bool, string, json" 380 | result = temp.pretty 381 | -------------------------------------------------------------------------------- /thatlib/thatlib.nim.cfg: -------------------------------------------------------------------------------- 1 | -d:lto 2 | -d:strip 3 | -d:noSignalHandler 4 | -d:nimBinaryStdFiles 5 | 6 | --gc:arc 7 | --app:lib 8 | --listFullPaths:off 9 | --excessiveStackTrace:off 10 | -------------------------------------------------------------------------------- /thatlib/thatlib.nimble: -------------------------------------------------------------------------------- 1 | requires "nimpy" 2 | -------------------------------------------------------------------------------- /thatlib/thatlib.py: -------------------------------------------------------------------------------- 1 | import nimporter 2 | import thatlibcore 3 | 4 | def cwd(): 5 | return thatlibcore.getCurrentDir() 6 | 7 | def home(): 8 | return thatlibcore.home() 9 | 10 | def mkdir(path): 11 | return thatlibcore.mkdir(path) 12 | 13 | def rmdir(path, check = False): 14 | return thatlibcore.rmdir(path, check) 15 | 16 | def is_file(path): 17 | return thatlibcore.is_file(path) 18 | 19 | def is_dir(path): 20 | return thatlibcore.is_dir(path) 21 | 22 | def exists(path): 23 | return thatlibcore.exists(path) 24 | 25 | def rename(source, destination): 26 | return thatlibcore.rename(source, destination) 27 | 28 | def replace(source, destination): 29 | return thatlibcore.replace(source, destination) 30 | 31 | def resolve(path): 32 | return thatlibcore.resolve(path) 33 | 34 | def absolute(path): 35 | return thatlibcore.absolute(path) 36 | 37 | def samefile(pathA, pathB): 38 | return thatlibcore.samefile(pathA, pathB) 39 | 40 | def is_absolute(path): 41 | return thatlibcore.is_absolute(path) 42 | 43 | def joinpath(paths): 44 | return thatlibcore.joinpath(paths) 45 | 46 | def with_suffix(path, ext): 47 | return thatlibcore.with_suffix(path, ext) 48 | 49 | def with_stem(path, stem): 50 | return thatlibcore.with_stem(path, stem) 51 | 52 | def with_name(path, name): 53 | return thatlibcore.with_name(path, name) 54 | 55 | def suffix(path): 56 | return thatlibcore.suffix(path) 57 | 58 | def stem(path): 59 | return thatlibcore.stem(path) 60 | 61 | def parent(path): 62 | return thatlibcore.parent(path) 63 | 64 | def write_bytes(path, data): 65 | return thatlibcore.write_bytes(path, data) 66 | 67 | def read_bytes(path): 68 | return thatlibcore.read_bytes(path) 69 | 70 | def is_relative_to(path, base): 71 | return thatlibcore.is_relative_to(path, base) 72 | 73 | def expanduser(path): 74 | return thatlibcore.expanduser(path) 75 | 76 | def as_uri(path): 77 | return thatlibcore.as_uri(path) 78 | 79 | def parts(path): 80 | return thatlibcore.parts(path) 81 | 82 | def chmod(path, permissions): 83 | return thatlibcore.chmod(path, permissions) 84 | 85 | 86 | # ^ API mimic from Python "pathlib" ################################## v Extras 87 | 88 | 89 | def is_root(): 90 | return thatlibcore.is_root() 91 | 92 | def symlink(source, destination): 93 | return thatlibcore.symlink(source, destination) 94 | 95 | def hardlink(source, destination): 96 | return thatlibcore.hardlink(source, destination) 97 | 98 | def env_vars_pairs(): 99 | return thatlibcore.env_vars_pairs() 100 | 101 | def parents(path): 102 | return thatlibcore.parents(path) 103 | 104 | def is_fs_casesensitive(): 105 | return thatlibcore.is_fs_casesensitive() 106 | 107 | def get_dynlib_format(): 108 | return thatlibcore.get_dynlib_format() 109 | 110 | def lines(path, start = 0, ends = 1): 111 | return thatlibcore.lines(path, start, ends) 112 | 113 | def line(path, line_number): 114 | return thatlibcore.line(path, line_number) 115 | 116 | def counted_lines(path): 117 | return thatlibcore.counted_lines(path) 118 | 119 | def tokenized(path): 120 | return thatlibcore.tokenized(path) 121 | 122 | def replaced(path, replacements): 123 | return thatlibcore.replaced(path, replacements) 124 | 125 | def normalized(path): 126 | return thatlibcore.normalized(path) 127 | 128 | def is_root_dir(path): 129 | return thatlibcore.is_root_dir(path) 130 | 131 | def get_suffix_index(path): 132 | return thatlibcore.get_suffix_index(path) 133 | 134 | def path_splitted(path): 135 | return thatlibcore.path_splitted(path) 136 | 137 | def compare_paths(pathA, pathB): 138 | return thatlibcore.compare_paths(pathA, pathB) 139 | 140 | def get_conf_dir(): 141 | return thatlibcore.get_conf_dir() 142 | 143 | def get_temporary_dir(): 144 | return thatlibcore.get_temporary_dir() 145 | 146 | def paths_quoted(paths): 147 | return thatlibcore.paths_quoted(paths) 148 | 149 | def is_file_newer(pathA, pathB): 150 | return thatlibcore.is_file_newer(pathA, pathB) 151 | 152 | def symlink_exists(path): 153 | return thatlibcore.symlink_exists(path) 154 | 155 | def get_exe(path, followSymlinks = True): 156 | return thatlibcore.get_exe(path, followSymlinks) 157 | 158 | def mksymlink(source, destination): 159 | return thatlibcore.mksymlink(source, destination) 160 | 161 | def get_symlink(path): 162 | return thatlibcore.get_symlink(path: string) 163 | 164 | def try_rmfile(path): 165 | return thatlibcore.try_rmfile(path) 166 | 167 | def exists_create_dir(path): 168 | return thatlibcore.exists_create_dir(path) 169 | 170 | def mkhardlink(source, destination): 171 | return thatlibcore.mkhardlink(source, destination) 172 | 173 | def get_size(path): 174 | return thatlibcore.get_size(path) 175 | 176 | def get_size_human(path): 177 | return thatlibcore.get_size_human(path) 178 | 179 | def is_hidden_path(path): 180 | return thatlibcore.is_hidden_path(path) 181 | 182 | def is_valid_path(path): 183 | return thatlibcore.is_valid_path(path) 184 | 185 | def get_md5(path): 186 | return thatlibcore.get_md5(path) 187 | 188 | def get_sha1(path): 189 | return thatlibcore.get_sha1(path) 190 | 191 | def copy_file_permissions(source, dest, ignore_errors = True) 192 | return thatlibcore.copy_file_permissions(source, dest, ignore_errors) 193 | 194 | def copy_dir_permissions(source, dest, ignore_errors = True) 195 | return thatlibcore.copy_dir_permissions(source, dest, ignore_errors) 196 | 197 | def get_file_info(path, follow_symlinks = True): 198 | return thatlibcore.get_file_info(path, follow_symlinks) 199 | 200 | def get_file_times_iso(path, follow_symlinks = True): 201 | return thatlibcore.get_file_times_iso(path, follow_symlinks) 202 | 203 | def get_file_times_unix(path, follow_symlinks = True): 204 | return thatlibcore.get_file_times_unix(path, follow_symlinks) 205 | 206 | def walk(folderpath; extensions = [], followlinks = False, yieldfiles = True, debugs = False, check_folders = False, prealloc = 99): 207 | return thatlibcore.walk(folderpath, extensions, followlinks, yieldfiles, debugs, check_folders, prealloc) 208 | 209 | def walk_glob(globpattern, prealloc = 99): 210 | return thatlibcore.walk_glob(globpattern, prealloc) 211 | 212 | def walk_simple(folderpath, relative = False, check_folders = False, prealloc = 99): 213 | return thatlibcore.walk_simple(folderpath, relative, check_folders, prealloc) 214 | 215 | def walk_folders(globpattern, prealloc = 99): 216 | return thatlibcore.walk_folders(globpattern, prealloc) 217 | 218 | def walk_files(globpattern, prealloc = 99): 219 | return thatlibcore.walk_files(globpattern, prealloc) 220 | 221 | def dotenv(path): 222 | return thatlibcore.dotenv(path) 223 | -------------------------------------------------------------------------------- /upload2pypi.sh: -------------------------------------------------------------------------------- 1 | twine upload --verbose dist/*.zip 2 | --------------------------------------------------------------------------------