├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── critical-bug.md
│ └── feature_request.md
├── pull_request_template.md
└── workflows
│ ├── python-package.yml
│ └── python-publish.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── README.md
├── assets
│ └── banner.png
├── docs
│ ├── blog.md
│ ├── cli.md
│ └── index.md
├── mkdocs.yml
└── polyglot-result.png
├── polyglot
├── __init__.py
├── __main__.py
├── arguments
│ ├── __init__.py
│ ├── arguments.py
│ └── position.py
├── core
│ ├── __init__.py
│ ├── beautify.py
│ ├── display.py
│ ├── extension.py
│ ├── ignore.py
│ ├── path.py
│ ├── polyglot.py
│ ├── project.py
│ ├── result.py
│ └── tree.py
├── exceptions
│ ├── __init__.py
│ ├── custom.py
│ └── exceptions.py
├── ext
│ ├── __init__.py
│ ├── dir.py
│ ├── env.py
│ ├── extensions.py
│ └── json.py
└── path.py
├── requirements.txt
├── setup.py
├── test.py
└── tests
├── __init__.py
├── context.py
├── test_arguments.py
├── test_file.py
└── test_polyglot.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: pranavbaburaj
5 |
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 |
28 | - OS: [e.g. iOS]
29 | - Browser [e.g. chrome, safari]
30 | - Version [e.g. 22]
31 |
32 | **Smartphone (please complete the following information):**
33 |
34 | - Device: [e.g. iPhone6]
35 | - OS: [e.g. iOS8.1]
36 | - Browser [e.g. stock browser, safari]
37 | - Version [e.g. 22]
38 |
39 | **Additional context**
40 | Add any other context about the problem here.
41 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/critical-bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Critical Bug
3 | about: Issues that should be fixed at the earliest
4 | title: Critical Bug
5 | labels: bug
6 | assignees: pranavbaburaj
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Related Issue
2 |
5 |
6 | Closes: #[issue number that will be closed through this PR]
7 |
8 |
9 | ## Describe the changes you've made
10 |
13 |
14 |
15 | ## Checklist:
16 |
17 |
21 |
22 | - [ ] My code follows the style guidelines of this project.
23 | - [ ] I have performed a self-review of my own code.
24 | - [ ] I have commented my code, particularly in hard-to-understand areas.
25 | - [ ] I have made corresponding changes to the documentation.
26 | - [ ] My changes generate no new warnings.
27 |
28 | ## Screenshots
29 |
30 | | Original | Updated |
31 | | :---------------------: | :------------------------: |
32 | | **original screenshot** | updated screenshot |
--------------------------------------------------------------------------------
/.github/workflows/python-package.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3 |
4 | name: Python package
5 |
6 | on:
7 | push:
8 | branches: [ main ]
9 | pull_request:
10 | branches: [ main ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version: [3.7, 3.8, 3.9]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v2
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | - name: Install dependencies
28 | run: |
29 | python -m pip install --upgrade pip
30 | python -m pip install flake8 pytest
31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
32 | - name: Lint with flake8
33 | run: |
34 | # stop the build if there are Python syntax errors or undefined names
35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
38 |
39 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | name: Upload Python Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | deploy:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Set up Python
18 | uses: actions/setup-python@v2
19 | with:
20 | python-version: '3.x'
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install setuptools wheel twine
25 | - name: Build and publish
26 | env:
27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
29 | run: |
30 | python setup.py sdist bdist_wheel
31 | twine upload dist/*
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | testing_json_storage.json
10 |
11 | ignore.polyglot
12 |
13 | .vscode/
14 |
15 | .vscode/
16 | language.yml
17 | language.yaml
18 |
19 | # Distribution / packaging
20 | .Python
21 | build/
22 | develop-eggs/
23 | dist/
24 | downloads/
25 | eggs/
26 | .eggs/
27 | lib/
28 | lib64/
29 | parts/
30 | sdist/
31 | var/
32 | wheels/
33 | pip-wheel-metadata/
34 | share/python-wheels/
35 | *.egg-info/
36 | .installed.cfg
37 | *.egg
38 | MANIFEST
39 |
40 | # PyInstaller
41 | # Usually these files are written by a python script from a template
42 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
43 | *.manifest
44 | *.spec
45 |
46 | # Installer logs
47 | pip-log.txt
48 | pip-delete-this-directory.txt
49 |
50 | # Unit test / coverage reports
51 | htmlcov/
52 | .tox/
53 | .nox/
54 | .coverage
55 | .coverage.*
56 | .cache
57 | nosetests.xml
58 | coverage.xml
59 | *.cover
60 | *.py,cover
61 | .hypothesis/
62 | .pytest_cache/
63 |
64 | # Translations
65 | *.mo
66 | *.pot
67 |
68 | # Django stuff:
69 | *.log
70 | local_settings.py
71 | db.sqlite3
72 | db.sqlite3-journal
73 |
74 | # Flask stuff:
75 | instance/
76 | .webassets-cache
77 |
78 | # Scrapy stuff:
79 | .scrapy
80 |
81 | # Sphinx documentation
82 | docs/_build/
83 |
84 | # PyBuilder
85 | target/
86 |
87 | # Jupyter Notebook
88 | .ipynb_checkpoints
89 |
90 | # IPython
91 | profile_default/
92 | ipython_config.py
93 |
94 | # pyenv
95 | .python-version
96 |
97 | # pipenv
98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
101 | # install all needed dependencies.
102 | #Pipfile.lock
103 |
104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105 | __pypackages__/
106 |
107 | # Celery stuff
108 | celerybeat-schedule
109 | celerybeat.pid
110 |
111 | # SageMath parsed files
112 | *.sage.py
113 |
114 | # Environments
115 | .env
116 | .venv
117 | env/
118 | venv/
119 | ENV/
120 | env.bak/
121 | venv.bak/
122 |
123 | # Spyder project settings
124 | .spyderproject
125 | .spyproject
126 |
127 | # Rope project settings
128 | .ropeproject
129 |
130 | # mkdocs documentation
131 | /site
132 |
133 | # mypy
134 | .mypy_cache/
135 | .dmypy.json
136 | dmypy.json
137 |
138 | # Pyre type checker
139 | .pyre/
140 |
141 | manifest.json
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Clone the repo
2 | Start by cloning the repo
3 | ```ps1
4 | git clone https://github.com/pranavbaburaj/polyglot.git
5 | ```
6 |
7 | ## Requirements
8 | - `Python` - Download and install python from the [official download page](https://www.python.org/downloads/)
9 | - `PIP` - PIP is the python package manager. Read more about it [here](https://pip.pypa.io/en/stable/user_guide/)
10 |
11 | ## Install the requirements
12 | ```ps1
13 | pip install -r requirements.txt
14 | ```
15 |
16 | ## Formatting the code
17 | Once you have finished adding the changes, make sure to format your code with formatter. It is recommended to use [`black`](https://github.com/psf/black)
18 |
19 | ```ps1
20 | <#
21 | Install the black code formatter
22 | using pip
23 | #>
24 | pip install black
25 |
26 | <#
27 | Format the code using black
28 | #>
29 | black .
30 | ```
31 |
32 | ## Create a pull request
33 | You can read more about pull requests [here](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Pranav Baburaj
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 |
2 |
3 |
4 |
5 |
6 |
Polyglot
7 |
8 |
9 | Find the percentage of programming languages used in your project
10 |
11 | 📖 Documentation
12 | ·
13 | Report a Bug
14 | ·
15 | Request Feature
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | ## About The Project
35 |
36 | Find the percentage of programming languages used in your project
37 |
38 |
39 |
40 | ## ⚡ Getting Started
41 |
42 | In order to get started, please make sure that you have [`python`](https://python.org) and `pip` installed on your computer.
43 |
44 |
45 | ### ⬇️ Installation
46 |
47 | - Install pip packages
48 |
49 | ```sh
50 | # install the python-polyglot package using pip
51 |
52 | pip3 install python-polyglot
53 | # or
54 | pip install python-polyglot
55 | ```
56 |
57 | ## 🎉 Usage
58 |
59 | Once Polyglot is all setup and good to go, implementing is easy as pie.
60 |
61 | ### 🔰 Initial Setup
62 |
63 | You can initialize Polyglot with the example below:
64 |
65 | ```python
66 | from polyglot.core import Polyglot
67 |
68 | dirname = "path/to/directory"
69 |
70 | polyglot = Polyglot(dirname)
71 | polyglot.show(display=True)
72 | ```
73 |
74 | Read the complete polyglot documentation [here](https://github.com/pranavbaburaj/polyglot/blob/main/docs/README.md)
75 |
76 |
77 |
78 |
79 | > Read more about the documentation [here](https://github.com/pranavbaburaj/polyglot/blob/main/docs/README.md)
80 |
81 |
109 |
110 |
111 |
112 | ## Contributing
113 |
114 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
115 |
116 | 1. [Fork](https://github.com/pranavbaburaj/polyglot/fork) the Project
117 | 2. Create your Feature Branch (`git checkout -b feature`)
118 | 3. Commit your Changes (`git commit -m 'Add some features'`)
119 | 4. Push to the Branch (`git push origin feature`)
120 | 5. Open a Pull Request
121 |
122 |
123 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Docs
2 | The documentation is divided into two parts.
3 | - [The Library documentation](./docs/index.md)
4 | - [The CLI documentation](./docs/cli.md)
--------------------------------------------------------------------------------
/docs/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lainq/polyglot/0a9b889bf02445f7b810e05587551ad0defc13b4/docs/assets/banner.png
--------------------------------------------------------------------------------
/docs/docs/blog.md:
--------------------------------------------------------------------------------
1 | I've recently been working on a side project called `polyglot`. Polyglot is a python module that finds the percentage of different programming languages used in your project.
2 |
3 | {% github pranavbaburaj/polyglot no-readme %}
4 | You can check it out on Github and also drop a star.
5 |
6 | ## Get Started
7 | In order to get started, you will need to have python and pip installed on your system.
8 |
9 | - Check the versions of `python` and `pip`
10 | ```
11 | python -v
12 | pip -v
13 | ```
14 | - Install `python-polyglot` using `pip`
15 |
16 | To install `python-polyglot` in your system, use
17 | ```
18 | pip install python-polyglot
19 | ```
20 |
21 | ## How to use it
22 | Once Polyglot is all set up and good to go, implementing is easy as pie.
23 | ```python
24 | from polyglot.core import Polyglot
25 |
26 | # dot(.) represents the current working directory
27 | dirname = "." or "path/to/dir"
28 |
29 | poly = Polyglot(".")
30 | poly.show()
31 |
32 | ```
33 | This prints out something similar
34 | ```
35 | +-------------------------+-------+
36 | | Language | files |
37 | +-------------------------+-------+
38 | | Ignore List | 5.88 |
39 | | GCC Machine Description | 11.76 |
40 | | Unknown | 5.88 |
41 | | Text | 5.88 |
42 | | Python | 64.71 |
43 | | JSON | 5.88 |
44 | +-------------------------+-------+
45 |
46 |
47 | +-------------------------+-------+
48 | | Language | lines |
49 | +-------------------------+-------+
50 | | Ignore List | 17.22 |
51 | | GCC Machine Description | 22.24 |
52 | | Unknown | 2.83 |
53 | | Text | 0.26 |
54 | | Python | 57.07 |
55 | | JSON | 0.39 |
56 | +-------------------------+-------+
57 | ```
58 |
59 | ### `Ignores`
60 | The `ignore` option is used to ignore specific files in the directory tree. For instance, if you don't want the `JSON` files to appear in the table, you can add the `.json` extension to a `polyglot-ignore` file and pass it as a parameter while creating the polyglot instance.
61 |
62 | - `Polyglot Ignores`
63 | Polyglot ignores are used to ignore
64 | specific files in the directory tree. They
65 | should have a `.polyglot` file extension.
66 | Polyglot Ignores as similar to gitignores
67 | and are easy to write with almost the same
68 | syntax.
69 |
70 | - `Writing a Polyglot ignore.`
71 | Create a `test.polyglot` file and add the
72 | files to ignore
73 | ```rb
74 | # for a specific file extension
75 | .json
76 |
77 | # for a specific folder
78 | dist/
79 |
80 | # for a specific file
81 | dub.sdl
82 | LICENSE
83 |
84 | # for specific folders in the directory
85 | ~.tox
86 | ```
87 | Once you have an ignore file, use it with polyglot like this
88 | ```python
89 | poly = Polyglot(dirname, ignore="test.polyglot")
90 | ```
91 |
92 | ### `Arguments`
93 | ```python
94 | from polyglot.arugments import Arguments
95 | ```
96 | The Polyglot Arguments is used to parse a list of arguments(`sys.argv[1:]` by default) and perform actions related to Polyglot.
97 |
98 | - You can either pass in arguments manually
99 | ```python
100 | args = Arguments(arguments=[
101 | "--show=True", "--dir=.", "--o=out.json", "--ignore=test.polyglot"
102 | ], return_value=False)
103 | ```
104 | or leave it blank to parse the command line arguments passed in along with the file
105 | ```python
106 | args = Arguments()
107 | ```
108 |
109 |
110 | - Start the argument parser
111 | ```python
112 | args.parse()
113 | ```
114 |
115 | The command-line parser has four main options,
116 | `--dir`(default:`current directory`) - The directory path
117 | `--show`(default:`True`) - Whether to display the table or not
118 | `--o`(default:`None`) - Outputs the data as JSON in the file
119 | `--ignore`(default:`None`) - The ignore file
120 |
121 | An example usage
122 | ```]
123 | python -B .py --dir=. --show=False
124 | ```
125 |
126 |
127 |
128 | Please star the project on GitHub if you like it. And thank you for scrolling.
129 |
--------------------------------------------------------------------------------
/docs/docs/cli.md:
--------------------------------------------------------------------------------
1 | # CLI
2 | The polyglot module comes along with a command line application. The cli can be accessed using `polyglot` or `pgt`
3 |
4 | ## `Stats`
5 | ```ps1
6 | polyglot stats
7 | ```
8 | #### Parameters
9 | - `--dir`:The directory to check the stats
10 | - Default : Current directory
11 | - `--ignore`: Files to ignore
12 | - Defult: By default the cli searches for files with a `.polyglot`extension
13 | - `--detect`: The language detection file
14 | - `--fmt`: `l` for comparison based on loc, `f` for files.
15 | - `--output`: Store the stats output into a file. Only json files and toml files are allowed.
16 |
17 | ```ps1
18 | polyglot stats --dir=/home/Documents --ignore --fmt=l --output=output.toml
19 | ```
20 |
21 | ## `Tree`
22 | ```ps1
23 | polyglot tree
24 | ```
25 |
26 | #### Parameters
27 | - `--dir`: The directory. Set to the current directory by default.
28 |
29 | ## `dir` or `ls`
30 | ```ps1
31 | polyglot ls
32 | polyglot dir
33 | ```
34 |
35 | ## `up`
36 | Update your cli
37 | ```
38 | polyglot up
39 | ```
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/docs/index.md:
--------------------------------------------------------------------------------
1 | # Polyglot
2 |
3 |
4 |
5 | ## `Polyglot`
6 |
7 | The `polyglot.core.Polyglot` is the main class of polyglot the polyglot module.
8 |
9 | ```python
10 | from polyglot.core import Polyglot
11 | ```
12 |
13 | Initialize a polyglot instance
14 |
15 | ```python
16 | polyglot = Polyglot("path/to/directory", ignore="example.polyglot")
17 | ```
18 |
19 | Polyglot takes in to parameters, the path to the directory and the ingore file
20 |
21 | - directory_name(**required**) - `string` The path of the directory
22 | - ignore - The ignore filename
23 |
24 | The ignore file should have a `.polyglot` file extension and has a syntax similar to a `.gitignore` file.
25 | - `.` for file extensions
26 | - `/` for folders
27 | - `` for files
28 | ```rb
29 | # for a specific file extension
30 | .json
31 |
32 | # for a specific folder
33 | dist/
34 |
35 | # for a specific file
36 | dub.sdl
37 | LICENSE
38 |
39 | # for specific folders in the directory
40 | ~.tox
41 | ```
42 |
43 |
44 | and use the file with the polyglot object
45 | ```python
46 | poly = Polyglot(".", "example.polyglot")
47 | ```
48 |
49 | Getting information from the polyglot object
50 |
51 | ```python
52 | polyglot.show(language_detection_file="language.yml", display=True)
53 | ```
54 |
55 | The `show` method takes in two parameters, the language detection file as well as the disply option
56 |
57 | - language_detection_file(**optional**) - `string` The yaml file containing information about all the languages. By default, the `language_detection_file` is set to `None`. and the file is downloaded from the internet.
58 | - display(**optional**) - `bool` Whether to output the table on the console or not. The show method returns a dict containing information about the files.
59 |
60 | ## `Tree`
61 | The `polyglot.core.tree` module helps us to generate a tree of the current directory
62 | ```py
63 | from polyglot.core.tree import Tree
64 |
65 | tree = Tree("path/to/directory").generate()
66 | ```
67 |
68 | ```ps1
69 | ├── colors.ts
70 | ├── gists
71 | │ ├── gist.ts
72 | │ └── new.ts
73 | ├── interface.ts
74 | ├── json
75 | │ └── colors.json
76 | ├── repos.ts
77 | └── user
78 | └── user.ts
79 | ```
80 |
81 | ## `Arguments`
82 |
83 | The `polyglot.arguments.Arguments` helps you parse a set of arguments and execute functions accordingly.
84 |
85 | ```python
86 | from polyglot.arugments import Arguments
87 | ```
88 |
89 | passing in arguments
90 |
91 | ```python
92 | args = Arguments(arguments=[], return_value=False)
93 | args.parse()
94 | ```
95 |
96 | - arguments(**optional**) - `list` The set of arguments. By default, the arguments in set to `sys.argv[1:]`.
97 | - return_value(**optional**) - `bool` Whether to return anything or not
98 |
99 | ## `Project`
100 | The `polyglot.core.project.Project` is used to generate folders and files.
101 |
102 | To create a `Project` object
103 | ```python
104 | from polyglot.core.project import Project, ProjectFiles
105 |
106 | # `.` for the current directory
107 | project = Project("project-name", ProjectFiles(
108 | files=["file1", "dir1/file1"],
109 | folders=["dir2", "some-unknown-folder"]
110 | ))
111 | ```
112 | To generate the directories
113 | ```py
114 | project.create(clean=False)
115 | ```
116 | The `create` function takes in the `clean` parameter which determines whether to clean the project directory if it already exists. The default value is set to `False`
117 |
118 | ## `polyglot.ext`
119 |
120 | ### `Env`
121 | The `polyglot.ext.env.Env` helps to load variables defined in a `.env` into the process environment variables
122 | ```py
123 | from polyglot.ext.env import Env
124 | env = Env()
125 | ```
126 |
127 | ### `directory` or `ls`
128 | The `polyglot.ext.dir` outputs something similar to an `ls` command in linux.
129 | ```python
130 | from polyglot.ext.dir import directory, ls
131 |
132 | directory("path/to/folder")
133 | # or
134 | ls("path/to/folder")
135 |
136 | ```
137 |
--------------------------------------------------------------------------------
/docs/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Polyglot
2 | nav :
3 | - Home : index.md
4 | - Blog : blog.md
5 | theme: material
6 |
--------------------------------------------------------------------------------
/docs/polyglot-result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lainq/polyglot/0a9b889bf02445f7b810e05587551ad0defc13b4/docs/polyglot-result.png
--------------------------------------------------------------------------------
/polyglot/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | """
25 |
26 | from .core import Polyglot
27 |
28 | __version__ = "4.2.9"
29 |
--------------------------------------------------------------------------------
/polyglot/__main__.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import json
4 | import pathlib
5 | import pip
6 |
7 | from clint.textui import colored
8 |
9 | from polyglot.core.polyglot import Polyglot
10 | from polyglot.core.project import Project, ProjectFiles
11 | from polyglot.core.tree import Tree
12 | from polyglot.ext.dir import ls
13 |
14 | COMMANDS = ["stats", "project", "tree", "dir", "ls", "help", "up"]
15 | DESCRIPTIONS = [
16 | "--dir= --ignore= --detect= --fmt= --output=",
17 | "--manifest=",
18 | "--dir=",
19 | "--dir=",
20 | "--dir=",
21 | "",
22 | "",
23 | ]
24 |
25 |
26 | class EventLogger(object):
27 | @staticmethod
28 | def info(message):
29 | print(colored.cyan(f"INFO:{message}"))
30 |
31 | @staticmethod
32 | def warning(message):
33 | print(colored.yellow(f"WARNING:{message}"))
34 |
35 | @staticmethod
36 | def error(message):
37 | print(colored.red(f"ERROR:{message}"))
38 |
39 |
40 | class CommandLineException(object):
41 | def __init__(self, message, suggestion=None, fatal=True):
42 | self.message = message
43 | self.suggestion = suggestion
44 | self.is_fatal = fatal
45 |
46 | self.create_exception()
47 |
48 | def create_exception(self):
49 | """Create an exception and exit the program if
50 | the exception is fatal
51 | """
52 | print(colored.red(f"ERROR:{self.message}"))
53 | if self.suggestion:
54 | print(colored.yellow(self.suggestion.strip()))
55 | if self.is_fatal:
56 | sys.exit(1)
57 |
58 |
59 | class ArgumentParser(object):
60 | class _ArgumentParserResults(object):
61 | def __init__(self, command, parameters):
62 | self.command = command
63 | self.parameters = parameters
64 |
65 | def __init__(self, arguments):
66 | self.arguments = arguments
67 |
68 | def create_argument_parser(self):
69 | """
70 | Parse the arguments into different commands
71 | and parameters. The first element in the
72 | argv list is considered to be the command.
73 |
74 | All the following elements should start with --
75 | and are considered as parameters
76 | """
77 | command, parameters = None, {}
78 | for index, current in enumerate(self.arguments):
79 | if index == 0:
80 | command = current
81 | continue
82 |
83 | if not current.startswith("--"):
84 | exception = CommandLineException(
85 | f"Invalid parameter {current}",
86 | f"Parameters should start with double hiphens ",
87 | )
88 |
89 | statement = current.split("=")
90 | parameter_key, value = statement[0], -2
91 | if len(statement) > 1:
92 | value = "=".join(map(str, statement[1:]))
93 | parameters.setdefault(parameter_key, value)
94 | return self._ArgumentParserResults(command, parameters)
95 |
96 |
97 | class HelpMessage(object):
98 | def __init__(self, commands, descriptions):
99 | assert isinstance(commands, list)
100 | self.commands = commands
101 | self.descriptions = descriptions
102 |
103 | self.create_help_string()
104 |
105 | def create_help_string(self):
106 | statements = ["usage: polyglot =", ""]
107 |
108 | for index, command in enumerate(self.commands):
109 | statements.append(
110 | f"{command}{self.spaces(command, COMMANDS)} -> {self.descriptions[index]}"
111 | )
112 | print("\n".join(statements))
113 |
114 | def spaces(self, command, commands):
115 | largest = max(
116 | [len(commands[current_index]) for current_index in range(len(commands))]
117 | )
118 | return "".join([" " for index in range(largest - len(command))])
119 |
120 |
121 | class LanguageStats(object):
122 | def __init__(self, parameters):
123 | self.directory = parameters.get("--dir") or os.getcwd()
124 | self.ignore = parameters.get("--ignore") or -1
125 | self.language_file = (
126 | None
127 | if (parameters.get("--detect") or None) == -2
128 | else parameters.get("--detect")
129 | )
130 | self.fmt = parameters.get("--fmt")
131 | self.output = parameters.get("--output") or None
132 |
133 | if self.fmt not in ["l", "f", "L", "F"]:
134 | self.fmt = None
135 | else:
136 | self.fmt = self.fmt.lower()
137 |
138 | if self.ignore == -2:
139 | self.ignore = self.__find_ignore_file()
140 | else:
141 | self.ignore = None
142 |
143 | try:
144 | polyglot = Polyglot(self.directory, self.ignore)
145 | polyglot.show(self.language_file, True, self.fmt, self.output)
146 | except Exception as exception:
147 | EventLogger.error(exception.__str__())
148 | sys.exit(1)
149 |
150 | def __find_ignore_file(self):
151 | if not os.path.isdir(self.directory):
152 | _ = CommandLineException(f"{self.directory} is not a directory")
153 | return None
154 | files = list(
155 | filter(
156 | lambda current_element: current_element.endswith(".polyglot"),
157 | os.listdir(self.directory),
158 | )
159 | )
160 | if len(files) == 0:
161 | EventLogger.error(f"Could not find an ignore file in {self.directory}")
162 | return None
163 |
164 | if len(files) > 1:
165 | EventLogger.warning(f"Found {len(files)} ignore files")
166 |
167 | ignore_filename = files[0]
168 | EventLogger.info(f"{ignore_filename} is taken as the ignore file")
169 | return ignore_filename
170 |
171 |
172 | class ListDirectories(object):
173 | def __init__(self, directory, only_dirs):
174 | self.directory = directory
175 | self.dirs = only_dirs
176 |
177 | self.list_directory_content()
178 |
179 | def list_directory_content(self):
180 | for filename in self.content:
181 | current_path = os.path.join(self.directory, filename)
182 | size = f"[size:{os.stat(current_path).st_size} bytes]"
183 | color = colored.green if os.path.isfile(current_path) else colored.blue
184 | print(f"{color(filename)} -> {colored.yellow(size)}")
185 |
186 | @property
187 | def content(self):
188 | return list(filter(self.file_filter_function, os.listdir(self.directory)))
189 |
190 | def file_filter_function(self, filename):
191 | if self.dirs:
192 | return os.path.isdir(filename)
193 | return True
194 |
195 |
196 | def search_for_manifest(manifest_filename):
197 | filename = os.path.join(os.getcwd(), manifest_filename)
198 | if not os.path.isfile(filename):
199 | _ = CommandLineException(f"{manifest_filename} does not exist")
200 | try:
201 | with open(filename, "r") as file_reader:
202 | return json.load(file_reader)
203 | except Exception as exception:
204 | CommandLineException(exception.__str__())
205 |
206 |
207 | def command_executor(results):
208 | command, params = results.command, results.parameters
209 | command_directory = params.get("--dir") or os.getcwd()
210 | if command_directory == -2:
211 | command_directory = os.getcwd()
212 | if not os.path.isdir(command_directory):
213 | EventLogger.error(f"{command_directory} is not a directory")
214 | return None
215 | if command == "stats":
216 | _ = LanguageStats(params)
217 | elif command == "project":
218 | manifest_file = params.get("--manifest") or "manifest.json"
219 | if manifest_file == -2:
220 | manifest_file = "manifest.json"
221 | manifest_data = search_for_manifest(manifest_file)
222 |
223 | name, files, folders = (
224 | manifest_data.get("name") or ".",
225 | manifest_data.get("files") or {},
226 | manifest_data.get("directories") or manifest_data.get("folders") or [],
227 | )
228 | try:
229 | project = Project(name, ProjectFiles(files, folders))
230 | project.create()
231 | except Exception as ProjectException:
232 | EventLogger.error(ProjectException.__str__())
233 | sys.exit(1)
234 | elif command == "tree":
235 | directory = params.get("--dir") or os.getcwd()
236 | if directory == -2:
237 | directory = os.getcwd()
238 | if not os.path.isdir(directory):
239 | EventLogger.error(f"{directory} is not a directory")
240 | return None
241 |
242 | try:
243 | tree = Tree(directory)
244 | tree.generate()
245 | except Exception as tree_exception:
246 | EventLogger.error(tree_exception.__str__())
247 | sys.exit(1)
248 | elif command == "dir":
249 | ls(command_directory)
250 | elif command == "ls":
251 | dirs = ListDirectories(command_directory, params.get("--only-dirs"))
252 | elif command == "help":
253 | help = HelpMessage(COMMANDS, DESCRIPTIONS)
254 | elif command == "up":
255 | pip.main(["install", "python-polyglot", "--upgrade"])
256 |
257 |
258 | class Properties(object):
259 | def __init__(self, path):
260 | assert os.path.exists(path), f"{path} does not exist"
261 | self.path = self.find_file_path(path)
262 | self.properties_command()
263 |
264 | def find_file_path(self, path):
265 | if path == ".":
266 | return os.getcwd()
267 | elif path == "..":
268 | return os.path.dirname(path)
269 |
270 | return path
271 |
272 | def properties_command(self):
273 | print(
274 | colored.green(os.path.basename(self.path), bold=True),
275 | colored.yellow(f"[{self.file_type}]"),
276 | )
277 | self.draw_seperator()
278 |
279 | properties = self.properties
280 | for property in properties:
281 | print(
282 | colored.cyan(f"{property} -> "),
283 | colored.yellow(properties.get(property)),
284 | )
285 |
286 | def draw_seperator(self):
287 | length = len(self.basename) + (len(self.file_type) + 3)
288 | for index in range(length):
289 | print(colored.yellow("-"), end=("\n" if index + 1 == length else ""))
290 |
291 | @property
292 | def properties(self):
293 | return {
294 | "type": self.file_type,
295 | "extension": self.file_extension,
296 | "parent": self.find_file_path(pathlib.Path(self.path).parent.__str__()),
297 | "size": os.stat(self.path).st_size,
298 | }
299 |
300 | @property
301 | def file_extension(self):
302 | if os.path.isdir(self.path):
303 | return ""
304 | split_path = self.basename.split(".")
305 | if len(split_path[0]) == 0 or split_path[0] == self.basename:
306 | return ""
307 | return split_path[-1]
308 |
309 | @property
310 | def basename(self):
311 | return os.path.basename(self.path)
312 |
313 | @property
314 | def file_type(self):
315 | return "DIR" if os.path.isdir(self.path) else "FILE"
316 |
317 |
318 | def main():
319 | arguments = sys.argv[1:]
320 | if len(arguments) == 0:
321 | return 1
322 | argument_parser = ArgumentParser(arguments)
323 | results = argument_parser.create_argument_parser()
324 | if not results.command:
325 | return 1
326 |
327 | if results.command.strip().__len__() == 0:
328 | return 1
329 |
330 | if results.command not in COMMANDS:
331 | try:
332 | Properties(results.command)
333 | except AssertionError as exception:
334 | EventLogger.error(exception.__str__())
335 | sys.exit()
336 | command_executor(results)
337 |
338 |
339 | if __name__ == "__main__":
340 | exit_status = main()
341 | if exit_status == 1:
342 | HelpMessage(COMMANDS, DESCRIPTIONS)
343 |
--------------------------------------------------------------------------------
/polyglot/arguments/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | """
25 |
26 | from .arguments import Arguments
27 |
--------------------------------------------------------------------------------
/polyglot/arguments/arguments.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import json
4 |
5 | from polyglot.arguments.position import Position
6 | from polyglot.exceptions.custom import PolyglotException
7 | from polyglot.core.polyglot import Polyglot
8 |
9 |
10 | class Arguments(object):
11 | """
12 | Pass a set of arguments which is parsed and
13 | later executed
14 |
15 | Attributes-
16 | arguments -- The arguments to parse
17 | return_value -- Whether to return any value
18 | position -- The lexer postion
19 |
20 | """
21 |
22 | def __init__(self, arguments=None, return_value=False):
23 | self.arguments = sys.argv[1:] if not arguments else arguments
24 | self.return_value = return_value
25 |
26 | self.position = Position(0)
27 |
28 | def parse(self):
29 | """
30 | Parse the arguments and validate
31 | them . Also execute the functions
32 | """
33 | assert isinstance(self.arguments, list)
34 | valid_flags = ["--dir", "--o", "--show", "--ignore"]
35 |
36 | parameters = {"dir": os.getcwd(), "o": None, "show": str(True), "ignore": ""}
37 |
38 | current_character = self.position.current_character(self.arguments)
39 | while current_character is not None:
40 | if not self.is_valid_flag(valid_flags, current_character):
41 | exception = PolyglotException(
42 | f"{current_character} is not recogonised as a valid parmeter",
43 | f"Try again with valid parameters",
44 | fatal=False,
45 | )
46 | return None
47 | character_key = current_character[2:]
48 | if "=" not in character_key:
49 | exception = PolyglotException(
50 | f"User equal-to(=) to add value to parameters",
51 | f"Try again with valid values",
52 | fatal=False,
53 | )
54 | return None
55 |
56 | if character_key.count("=") > 1:
57 | exception = PolyglotException(
58 | f"More than one assignments for the same parameter",
59 | f"Try again with valid values",
60 | fatal=False,
61 | )
62 | return None
63 |
64 | data = character_key.split("=")
65 | key, value = data[0], data[1]
66 |
67 | parameters[key] = value
68 |
69 | self.position.increment()
70 | current_character = self.position.current_character(self.arguments)
71 |
72 | return_data = self.validate_parameters(parameters)
73 | if return_data == None:
74 | return return_data
75 | else:
76 | polyglot = Polyglot(parameters["dir"], ignore=parameters["ignore"])
77 | data = polyglot.show(display=parameters["show"])
78 |
79 | if parameters["o"]:
80 | with open(parameters["o"], "w") as writer:
81 | writer.write(json.dumps(data))
82 |
83 | if self.return_value:
84 | return data
85 |
86 | def is_valid_flag(self, valid_cases, current_character):
87 | for valid_case_index in range(len(valid_cases)):
88 | if current_character.startswith(valid_cases[valid_case_index]):
89 | return True
90 |
91 | return False
92 |
93 | def validate_parameters(self, parameters):
94 | if parameters["show"] not in [str(True), str(False)]:
95 | exception = PolyglotException(
96 | "Invalid value for paramter show", "Try again", fatal=False
97 | )
98 | return None
99 |
100 | parameters["show"] = bool(parameters["show"])
101 | parameters["ignore"] = parameters["ignore"].split(",")
102 | return parameters
103 |
--------------------------------------------------------------------------------
/polyglot/arguments/position.py:
--------------------------------------------------------------------------------
1 | class Position(object):
2 | def __init__(self, initial_position):
3 | assert isinstance(initial_position, int), "Expected an integer"
4 | self.position = initial_position
5 |
6 | def increment(self, increment_by=1):
7 | self.position += 1
8 | return self.position
9 |
10 | def decrement(self, decrement_by=1):
11 | self.position -= 1
12 | return self.position
13 |
14 | def current_character(self, data, increment_value=False):
15 | assert isinstance(data, list), "Data expected to be a string"
16 | if len(data) == self.position:
17 | return None
18 |
19 | return_value = data[self.position]
20 | if increment_value:
21 | self.increment()
22 |
23 | return return_value
24 |
--------------------------------------------------------------------------------
/polyglot/core/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | """
25 |
26 | from .polyglot import Polyglot
27 |
--------------------------------------------------------------------------------
/polyglot/core/beautify.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | from clint.textui import colored
4 | from collections.abc import Iterable
5 |
6 | from polyglot.path import DirectoryError
7 |
8 |
9 | class _ExtensionsionType:
10 | required_type = Iterable
11 |
12 | def __init__(self, placeholder):
13 | self.value = placeholder
14 | self.check_value_type(self.value)
15 |
16 | def check_value_type(self, value):
17 | """
18 | Make sure that the key value is an
19 | iterable, either a tuple, list, string
20 | or dictionary
21 |
22 | Args:
23 | value (any): The value to check the type of
24 |
25 | Raises:
26 | TypeError: Raises a TypeError when the value is not
27 | an iterable
28 | """
29 | if not isinstance(value, self.required_type):
30 | raise TypeError(colored.red(f"{value} not an iterable"))
31 |
32 | def __repr__(self):
33 | return self.value
34 |
35 |
36 | class ExtensionMap(object):
37 | def __init__(self, extensions={}):
38 | assert isinstance(extensions, dict), "Extensions expected to be a dict"
39 | self.extensions = {}
40 | self.__add_all_extensions(extensions)
41 |
42 | def __add_all_extensions(self, extensions):
43 | """
44 | Add all the extensions from the dict passed
45 | in as a parameter
46 |
47 | Args:
48 | extensions (dict): The dict to copy
49 | """
50 | for key in extensions:
51 | _ExtensionsionType(extensions[key])
52 | self.extensions = extensions
53 |
54 | def add(self, folder, extensions):
55 | """
56 | Add a new folder to the extension map with its
57 | associated extension array
58 |
59 | Args:
60 | folder (string): The name of the folder
61 | extensions (Iterable): The extensions to search for
62 |
63 | Raises:
64 | KeyError: Raises an error when the key already
65 | exists in the map
66 | """
67 | if self.extensions.get(folder):
68 | raise KeyError(f"{folder} already exists in the map")
69 |
70 | _ExtensionsionType(extensions)
71 | self.extensions.setdefault(folder, extensions)
72 |
73 | def remove(self, folder):
74 | if folder not in self.extensions:
75 | raise KeyError(f"{folder} does not exist")
76 |
77 | del self.extensions[folder]
78 |
79 | def get(self):
80 | return self.extensions
81 |
82 | def __repr__(self):
83 | return str(self.extensions)
84 |
85 | def __str__(self):
86 | return self.__repr__()
87 |
88 | def __len__(self):
89 | return len(self.extensions)
90 |
91 |
92 | class _Prompt(object):
93 | def __init__(self, prompt, options, require=True):
94 | self.prompt = prompt
95 | self.options = options
96 | self.require = require
97 |
98 | def create_prompt(self):
99 | data = None
100 | while data not in self.options:
101 | data = input(f"{self.prompt} (y/n) ")
102 | return data == "y"
103 |
104 |
105 | class _Logs(object):
106 | def __init__(self, log):
107 | self.log_messages = log
108 | self.counter = 0
109 |
110 | def log(self, message, critical=False):
111 | color = colored.red if critical else colored.green
112 | if self.log_messages:
113 | self.counter += 1
114 | print(color(f"LOG[{self.counter}] {message}"))
115 |
116 |
117 | class Beautify(object):
118 | def __init__(self, directory, extensions, prompt=True, log=True):
119 | assert isinstance(
120 | extensions, ExtensionMap
121 | ), "extensions expected to be an extension map"
122 |
123 | self.directory = self.__is_directory(directory)
124 | self.extensions = extensions
125 | self.log = _Logs(log)
126 |
127 | self.clean_directory(prompt)
128 |
129 | def clean_directory(self, prompt):
130 | if prompt:
131 | data = _Prompt(
132 | "Do you want to clear the directory", ["y", "n"]
133 | ).create_prompt()
134 | if data:
135 | self.__clean()
136 |
137 | return None
138 |
139 | self.__clean()
140 |
141 | def __clean(self):
142 | replace_files = {}
143 | data = self.extensions.get()
144 | for filename in os.listdir(self.directory):
145 | folder = self.__get_extension_folder(filename, data)
146 | if not folder:
147 | continue
148 |
149 | path = os.path.join(self.directory, folder)
150 | move_location = os.path.join(path, filename)
151 |
152 | if not os.path.exists(path) or not os.path.isdir(path):
153 | os.mkdir(path)
154 |
155 | shutil.move(os.path.join(self.directory, filename), move_location)
156 | self.log.log(
157 | f"Moved Successfully [{os.path.join(self.directory, filename)} => {move_location}]"
158 | )
159 |
160 | def __get_extension_folder(self, extension, data):
161 | for foldername in data:
162 | extension_list = data[foldername]
163 | for file_extension in extension_list:
164 | if extension.endswith(file_extension):
165 | return foldername
166 | return None
167 |
168 | def __is_directory(self, directory):
169 | is_directory = os.path.isdir(directory)
170 | if not is_directory:
171 | raise DirectoryError(f"{directory} not a directory")
172 |
173 | return directory
174 |
--------------------------------------------------------------------------------
/polyglot/core/display.py:
--------------------------------------------------------------------------------
1 | from prettytable import PrettyTable
2 | from clint.textui import colored
3 |
4 |
5 | class Display(object):
6 | def __init__(self, display_text):
7 | assert isinstance(display_text, dict), "Expected a dict"
8 | self.text = display_text
9 |
10 | if not "files" in self.text or not "lines" in self.text:
11 | raise NameError("Cannot find required keys - lines, files")
12 |
13 | self.display_output()
14 |
15 | def display_output(self):
16 | """
17 | Verify the dict elements to be dicts
18 | and then continue to print out
19 | the dict data in tabular form using
20 | prettytable.
21 | """
22 | self.verify_text()
23 |
24 | for display_text_type in self.text:
25 | if bool(self.text[display_text_type]):
26 | print("\n")
27 | table = PrettyTable()
28 | table.field_names = [
29 | "Language",
30 | display_text_type.capitalize(),
31 | "Total",
32 | "Blank",
33 | ]
34 |
35 | for data in self.text[display_text_type]:
36 | current_data = self.text[display_text_type][data]
37 | current_row = [
38 | data,
39 | current_data.get("data"),
40 | current_data.get("total"),
41 | current_data.get("blank"),
42 | ]
43 |
44 | table.add_rows([current_row])
45 | print(colored.yellow(table))
46 |
47 | def verify_text(self):
48 | assert isinstance(self.text["files"], dict), "Files expected to be a dict"
49 | assert isinstance(self.text["lines"], dict), "lines expected to be a dict"
50 |
--------------------------------------------------------------------------------
/polyglot/core/extension.py:
--------------------------------------------------------------------------------
1 | import os
2 | import requests
3 | import yaml
4 |
5 | from polyglot.core.path import LanguageJSON
6 |
7 | LANGUAGE_FILE = "https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml"
8 |
9 |
10 | def validate_argument_types(values, types, message):
11 | assert len(values) == len(types), "Values and types should have the same length"
12 | for index in range(len(values)):
13 | assert isinstance(values[index], types[index]), str(message)
14 | return True
15 |
16 |
17 | def install_files(read_url, write_file_dir, filename, extension):
18 | assert isinstance(read_url, str), "Read url expected to be a string"
19 | assert isinstance(write_file_dir, str), "Write path expected to be a string"
20 |
21 | filename = os.path.join(write_file_dir, f"{filename}.{extension}")
22 | try:
23 | with open(filename, "wb") as file_writer:
24 | file_writer.write(requests.get(read_url, allow_redirects=True).content)
25 | except Exception as exception:
26 | raise Exception
27 |
28 | return filename
29 |
30 |
31 | class Extensions(object):
32 | def __init__(self, language_file, display, files):
33 |
34 | self.language_detection_file = language_file
35 | self.display_output = display
36 | self.filenames = files
37 |
38 | self.languages = {}
39 |
40 | self.content = self.remove_unwanted_keys(
41 | self.__create_language_file(self.language_detection_file)[0]
42 | )
43 |
44 | def get_extension_data(self):
45 | return self.__split_files(self.filenames, self.content)
46 |
47 | def __split_files(self, files, content):
48 | """
49 | Loop through each file in the files array
50 | and determine the language with the help of
51 | the language extension
52 | """
53 | for filename in files:
54 | language = self.__find_language_name(filename, content)
55 | if language not in self.languages:
56 | self.languages[language] = []
57 |
58 | self.languages[language].append(filename)
59 | return self.languages
60 |
61 | def __find_language_name(self, filename, content):
62 | extension = f".{filename.split('.')[-1]}"
63 | for language_key in content:
64 | if "extensions" not in content[language_key]:
65 | continue
66 |
67 | if extension in content[language_key]["extensions"]:
68 | return language_key
69 |
70 | return "Unknown file"
71 |
72 | def remove_unwanted_keys(self, file_content):
73 | """
74 | Remove all the unwanted keys from the
75 | file_content dictionary and only keep
76 | the 'extensions' key
77 | """
78 | for language in dict(file_content):
79 | assert isinstance(file_content[language], dict), "Expected a dict"
80 | for key in dict(file_content[language]):
81 | if key != "extensions":
82 | del file_content[language][key]
83 | return file_content
84 |
85 | def __create_language_file(self, language_file):
86 | """
87 | If language file is mentioned, and the file is a string
88 | return the filecontent and the number of lines
89 |
90 | Else, install the language file from the internet
91 | and return the file_content along with the number of lines
92 | """
93 | if language_file is not None and isinstance(language_file, str):
94 | if (
95 | not language_file.endswith(".yml")
96 | and not language_file.endswith(".json")
97 | and not language_file.endswith(".yaml")
98 | ):
99 | raise Exception("Language file expected to be a yaml or json file")
100 |
101 | if language_file.endswith(".json"):
102 | filename = LanguageJSON(language_file).convert_to_yaml()
103 | return Extensions.read_file_data(filename, True)
104 | else:
105 | return Extensions.read_file_data(language_file, True)
106 |
107 | return Extensions.read_file_data(
108 | install_files(LANGUAGE_FILE, os.getcwd(), "language", "yml"), True
109 | )
110 |
111 | @staticmethod
112 | def read_file_data(filename, is_yaml=False):
113 | """
114 | Read the specified filename and if the file
115 | is a yaml file,parse the yaml string
116 | using the yaml library
117 | """
118 | with open(filename, "r") as file_reader:
119 | file_content = file_reader.read()
120 | line_number_count = len(file_content.split("\n"))
121 |
122 | if not is_yaml:
123 | return file_content, line_number_count
124 |
125 | return yaml.safe_load(file_content), line_number_count
126 |
--------------------------------------------------------------------------------
/polyglot/core/ignore.py:
--------------------------------------------------------------------------------
1 | import os
2 | from clint.textui import colored
3 |
4 |
5 | class IgnoreFileError(FileNotFoundError):
6 | def __init__(self, message_data):
7 | self.error_message = colored.red(message_data.strip())
8 | super().__init__(self.error_message)
9 |
10 |
11 | class PolyglotExtensionError(Exception):
12 | def __init__(self, message_data):
13 | self.error_message = colored.red(message_data.strip())
14 | super().__init__(self.error_message)
15 |
16 |
17 | class Ignore(object):
18 | def __init__(self, ignore_list_filename):
19 | assert isinstance(ignore_list_filename, str)
20 |
21 | self.ignore_files = []
22 | self.ignore_list_filename = ignore_list_filename
23 | self.ignore_data = self.read_file(self.ignore_list_filename)
24 |
25 | self.files = None
26 |
27 | def create_ignore_files(self, files, directory):
28 | """
29 | Update the ignore files list
30 | based on information from the polyglot ignore
31 | file
32 | """
33 | self.files = files
34 | for ignore_data_line in self.ignore_data:
35 | if ignore_data_line.startswith("."):
36 | self.__find_file_extension(ignore_data_line)
37 | elif ignore_data_line.endswith("/"):
38 | self.find_dir_files(ignore_data_line[:-1])
39 | elif ignore_data_line.startswith("~"):
40 | self.add_root_dirs(ignore_data_line[1:], directory)
41 | else:
42 | self.add_files(ignore_data_line)
43 |
44 | self.add_root_dirs(".git", directory)
45 | return self.ignore_files
46 |
47 | def add_root_dirs(self, root, dirname):
48 | root_directory = os.path.join(dirname, root)
49 | if not self.files:
50 | return None
51 |
52 | for filename in self.files:
53 | if filename.startswith(root_directory):
54 | self.ignore_files.append(filename)
55 |
56 | def find_dir_files(self, directory_name):
57 | """
58 | Get all the files with the directory_name
59 | as the parent directory
60 | """
61 | if not self.files:
62 | return None
63 | for filename in self.files:
64 | dirname = os.path.basename(os.path.dirname(filename))
65 | if dirname == directory_name:
66 | self.ignore_files.append(filename)
67 |
68 | def add_files(self, data_line):
69 | """
70 | Added other files with the basename as
71 | the data_line
72 | """
73 | if not self.files:
74 | return None
75 | for filename in self.files:
76 | if os.path.basename(filename) == data_line:
77 | self.ignore_files.append(filename)
78 |
79 | def __find_file_extension(self, extension):
80 | """
81 | Add all the files with the specific
82 | file extension
83 | """
84 | if not self.files:
85 | return None
86 | for filename in self.files:
87 | if filename.endswith(extension):
88 | self.ignore_files.append(filename)
89 |
90 | @staticmethod
91 | def __find_all_files(ignore_files, ignore_extensions, files):
92 | for filename_index in range(len(files)):
93 | current_filename = files[filename_index]
94 | file_extension = current_filename.split(".")[-1]
95 | if current_filename.enswith(file_extension):
96 | ignore_files.append(current_filename)
97 | return ignore_files
98 |
99 | def read_file(self, read_file_name, ignore_text=True):
100 | """
101 | Valiate a file and read the file
102 | """
103 | if not os.path.exists(read_file_name) or not os.path.isfile(read_file_name):
104 | raise IgnoreFileError(f"{read_file_name} is not a valid file")
105 |
106 | if ignore_text and not read_file_name.endswith(".polyglot"):
107 | raise PolyglotExtensionError(
108 | f"Ignore files require to have a .polyglot file extension"
109 | )
110 |
111 | with open(read_file_name, "r") as file_reader:
112 | file_data = file_reader.read().split("\n")
113 |
114 | if not ignore_text:
115 | return file_data
116 |
117 | return Ignore.remove_specific_list_element(
118 | [
119 | (filename if len(filename.strip()) > 0 else None)
120 | for filename in file_data
121 | ],
122 | [None],
123 | )
124 |
125 | @staticmethod
126 | def remove_specific_list_element(list_data, remove_element):
127 | """
128 | Remove a specific list of elements from another
129 | list and return the new list
130 | """
131 | assert isinstance(list_data, list)
132 | return_array = []
133 | for element in list_data:
134 | if element not in remove_element:
135 | return_array.append(element)
136 |
137 | return return_array
138 |
--------------------------------------------------------------------------------
/polyglot/core/path.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import yaml
4 | from clint.textui import colored
5 |
6 |
7 | class LanguageException(FileNotFoundError):
8 | def __init__(self, text):
9 | self.message = colored.red(text)
10 | super().__init__(self.message)
11 |
12 |
13 | class LanguageJSON(object):
14 | def __init__(self, json_path, rename=None):
15 | self.path = json_path
16 | self.rename = self.create_new_name(rename)
17 |
18 | def create_new_name(self, rename):
19 | if not isinstance(rename, str):
20 | filename = self.path.split(".")[0]
21 | return f"{filename}.yml"
22 |
23 | return rename
24 |
25 | def convert_to_yaml(self):
26 | if not os.path.exists(self.path):
27 | raise LanguageException(f"Cannot find {self.path}")
28 | with open(self.path, "r", encoding="utf8") as file_reader:
29 | data = json.loads(file_reader.read())
30 |
31 | with open(self.path, "w", encoding="utf8") as yaml_writer:
32 | yaml.dump(data, yaml_writer, allow_unicode=True)
33 |
34 | os.rename(self.path, self.rename)
35 | return self.rename
36 |
--------------------------------------------------------------------------------
/polyglot/core/polyglot.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import yaml
4 | import toml
5 |
6 | from polyglot.core.extension import Extensions
7 | from polyglot.core.result import Result
8 | from polyglot.core.display import Display
9 | from polyglot.core.ignore import Ignore
10 |
11 | from polyglot.exceptions.exceptions import PolyglotFileNotFoundError
12 |
13 |
14 | class Polyglot(object):
15 | """
16 | The main polyglot class. An instance of this class
17 | is created by the user to use the core functions of
18 | the module
19 |
20 | Attributes:
21 | ignore -- The files to ignore
22 | directory -- The abspath of the directory to check
23 | files -- All the files inside of the directory
24 |
25 | """
26 |
27 | def __init__(self, directory_name: str, ignore=None):
28 | assert ignore == None or isinstance(
29 | ignore, str
30 | ), "Expected to be a string or None"
31 | self.ignore = ignore
32 | self.directory = Polyglot.find_directory_path(directory_name)
33 | self.files = self.find_directory_files(self.directory)
34 | if self.ignore:
35 | self.files = Ignore.remove_specific_list_element(
36 | self.files,
37 | Ignore(self.ignore).create_ignore_files(self.files, self.directory),
38 | )
39 |
40 | @staticmethod
41 | def find_directory_path(directory_path: str):
42 | """
43 | Determine the directory path based on
44 | the parameter. If the path is a dot(.) return the
45 | current working directory, else return
46 | the path if the path is a directory, else
47 | throw an error
48 | """
49 | assert isinstance(directory_path, str), "Path expected to be a string"
50 | if directory_path == ".":
51 | return os.getcwd()
52 |
53 | if os.path.isdir(directory_path):
54 | return directory_path
55 |
56 | raise PolyglotFileNotFoundError(f"{directory_path} does not exist")
57 |
58 | def __find_hidden_files(self, hidden, filepath):
59 | """
60 | Make sure that the root of the file
61 | is not hidden
62 | """
63 | hidden_root = [str(filepath).startswith(hidden_file) for hidden_file in hidden]
64 | return True in hidden_root
65 |
66 | def find_directory_files(self, directory):
67 | """
68 | Find all the files by walking through
69 | the directory tree
70 | """
71 | filenames = []
72 | hidden_directories = []
73 | for (root, dirs, files) in os.walk(directory, topdown=True):
74 | if not self.__find_hidden_files(hidden_directories, root):
75 | for filename in files:
76 | if filename.startswith(os.path.join(self.directory, ".git")):
77 | continue
78 | filenames.append(os.path.join(root, filename))
79 |
80 | return filenames
81 |
82 | def show(self, language_detection_file=None, display=True, fmt=None, output=None):
83 | DEFAULT_LANGUAGE_DETECTION_FILE = "language.yml"
84 | if language_detection_file is None:
85 | for filename in os.listdir(os.getcwd()):
86 | if filename == DEFAULT_LANGUAGE_DETECTION_FILE and os.path.isfile(
87 | filename
88 | ):
89 | language_detection_file = os.path.join(os.getcwd(), filename)
90 | break
91 |
92 | extensions = Extensions(language_detection_file, display, self.files)
93 | data = extensions.get_extension_data()
94 |
95 | result = Result(data).show_file_information()
96 | if display and fmt is None:
97 | display_text = Display(result)
98 | elif display and fmt is not None:
99 | if fmt.lower() == "l":
100 | result["files"] = {}
101 | display_text = Display(result)
102 | elif fmt.lower() == "f":
103 | result["lines"] = {}
104 | display_text = Display(result)
105 |
106 | if isinstance(output, str):
107 | with open(output, mode="w", encoding="utf8") as output_logger:
108 | if output.endswith(".yml") or output.endswith(".yaml"):
109 | yaml.dump(result, output_logger, allow_unicode=True)
110 | elif output.endswith(".toml"):
111 | output_logger.write(toml.dumps(result))
112 | else:
113 | output_logger.write(json.dumps(result, indent=4))
114 |
115 | return result
116 |
--------------------------------------------------------------------------------
/polyglot/core/project.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from polyglot.core import Polyglot
4 |
5 |
6 | class ProjectFiles(object):
7 | def __init__(self, files, folders):
8 | assert isinstance(files, dict), "Files expected to be a dict"
9 | self.files = files
10 | self.folders = folders
11 |
12 |
13 | class Project(object):
14 | def __init__(self, project_name, project_files, polyglot=False):
15 | assert isinstance(
16 | project_files, ProjectFiles
17 | ), "Parameter expected to be of type ProjectFiles"
18 | self.name = project_name
19 | self.files = project_files
20 | self.polyglot = polyglot
21 |
22 | if self.polyglot:
23 | self.files.files["ignore.polyglot"] = "language.yml"
24 |
25 | def create(self, clean=False):
26 | directory = self.__directory_path(self.name)
27 | if os.path.exists(directory) and os.path.isdir(directory):
28 | directory_length = len(os.listdir(directory))
29 | if directory_length > 0:
30 | if not clean:
31 | raise FileExistsError(f"{directory} already exists")
32 | return None
33 |
34 | os.rmdir(directory)
35 | os.mkdir(directory)
36 | else:
37 | os.mkdir(directory)
38 |
39 | self.__create_project_files(directory)
40 |
41 | def __create_project_files(self, directory):
42 | for filename in self.files.files:
43 | self.write_file_data(
44 | os.path.join(directory, filename), self.files.files.get(filename)
45 | )
46 |
47 | for folder in self.files.folders:
48 | if not os.path.isdir(folder):
49 | os.mkdir(os.path.join(directory, folder))
50 |
51 | if self.polyglot:
52 | polyglot = Polyglot(directory, "ignore.polyglot")
53 | polyglot.show(
54 | display=False, output=os.path.join(directory, "polyglot.json")
55 | )
56 |
57 | def write_file_data(self, filename, data=""):
58 | with open(filename, "w") as file_writer:
59 | file_writer.write(data)
60 |
61 | def __directory_path(self, project_name):
62 | assert isinstance(project_name, str), "Project name expected to be a string"
63 | if project_name == ".":
64 | return os.getcwd()
65 |
66 | return os.path.join(os.getcwd(), project_name)
67 |
--------------------------------------------------------------------------------
/polyglot/core/result.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from polyglot.core.ignore import Ignore
4 |
5 |
6 | class Result(object):
7 | def __init__(self, file_information):
8 | assert isinstance(file_information, dict), "Expected a dict"
9 |
10 | self.file_information = file_information
11 | self.data = {"files": {}, "lines": {}}
12 |
13 | def show_file_information(self):
14 | files = self.__find_by_files(self.file_information)
15 | lines = self.__find_by_lines(self.file_information)
16 |
17 | return self.data
18 |
19 | def __find_by_files(self, data):
20 | length = sum([len(data[key]) for key in data])
21 | for file_type in data:
22 | self.data["files"][file_type] = {
23 | "data": f"{round((len(data[file_type]) / length) * 100, 2)} %",
24 | "total": len(data[file_type]),
25 | "blank": len(
26 | Ignore.remove_specific_list_element(
27 | [
28 | os.path.getsize(filename) == 0
29 | for filename in data[file_type]
30 | ],
31 | [False],
32 | )
33 | ),
34 | }
35 |
36 | def __find_by_lines(self, data):
37 | lines = {}
38 | empty = {}
39 | for file_type in data:
40 | file_line_count = 0
41 | empty_line_count = 0
42 | for filename in data[file_type]:
43 | if not os.path.exists(filename):
44 | continue
45 |
46 | with open(filename, "r", errors="ignore") as line_counter:
47 | file_data = line_counter.read().split("\n")
48 |
49 | file_line_count += len(file_data)
50 | for line in file_data:
51 | if len(line.strip()) == 0:
52 | empty_line_count += 1
53 |
54 | lines[file_type] = file_line_count
55 | empty[file_type] = empty_line_count
56 | total_lines = sum([lines[key] for key in lines])
57 | for line_key in data:
58 | self.data["lines"][line_key] = {
59 | "data": f"{round((lines[line_key] / total_lines) * 100, 2)} %",
60 | "total": lines.get(line_key),
61 | "blank": empty.get(line_key),
62 | }
63 |
--------------------------------------------------------------------------------
/polyglot/core/tree.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pathlib
3 | import itertools
4 | import clint
5 |
6 |
7 | class UnknownPathError(FileNotFoundError):
8 | def __init__(self, error_message):
9 | self.message = clint.textui.colored.red(error_message)
10 |
11 | super.__init__(self.message)
12 |
13 |
14 | class Tree(object):
15 | space = " "
16 | branch = "│ "
17 | tree = "├── "
18 | last = "└── "
19 |
20 | def __init__(self, directory):
21 | self.directory = (
22 | directory
23 | if self.__verify_directory_path(
24 | os.getcwd() if directory == "." else directory
25 | )
26 | else None
27 | )
28 |
29 | def __verify_directory_path(self, directory):
30 | if not os.path.exists(directory) or not os.path.isdir(directory):
31 | return False
32 |
33 | return True
34 |
35 | def generate(self, level=-1, limit_to_directories=False, length_limit=None):
36 | if not self.directory:
37 | raise UnknownPathError(f"Cannot find {self.directory}")
38 |
39 | path = pathlib.Path(self.directory)
40 |
41 | def inner(dir_path: pathlib.Path, prefix: str = "", level=-1):
42 | if not level:
43 | return
44 | if limit_to_directories:
45 | contents = [d for d in dir_path.iterdir() if d.is_dir()]
46 | else:
47 | contents = list(dir_path.iterdir())
48 | pointers = [self.tree] * (len(contents) - 1) + [self.last]
49 | for pointer, path in zip(pointers, contents):
50 | root = os.path.abspath(os.path.dirname(path.absolute()))
51 | try:
52 | if path.is_dir():
53 | if ".git" in str(path.absolute()):
54 | continue
55 | yield prefix + pointer + path.name
56 | extension = self.branch if pointer == self.tree else self.space
57 | yield from inner(path, prefix=prefix + extension, level=level - 1)
58 | elif not limit_to_directories:
59 | yield prefix + pointer + path.name
60 | except PermissionError:
61 | continue
62 | try:
63 | iterator = inner(path, level=level)
64 | for line in itertools.islice(iterator, length_limit):
65 | print(clint.textui.colored.cyan(line))
66 | if next(iterator, None):
67 | print(
68 | clint.textui.colored.red(
69 | f"... length_limit, {length_limit}, reached, counted:"
70 | )
71 | )
72 | except KeyboardInterrupt:
73 | return None
74 |
--------------------------------------------------------------------------------
/polyglot/exceptions/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | """
25 | from .custom import PolyglotException
26 | from .exceptions import *
27 |
--------------------------------------------------------------------------------
/polyglot/exceptions/custom.py:
--------------------------------------------------------------------------------
1 | import sys as sys
2 | import logging as logging
3 | import time as time
4 | from clint.textui import colored
5 |
6 |
7 | def stop_current_application(exit_reason=None, set_timeout=0):
8 | assert isinstance(set_timeout, int), "Expected an integer"
9 | if exit_reason is not None:
10 | logging.error(f"Exiting application [{exit_reason}]")
11 |
12 | time.sleep(set_timeout)
13 | sys.exit()
14 |
15 |
16 | class PolyglotException(object):
17 | def __init__(self, error_message, suggestion=None, timeout=None, fatal=True):
18 | assert self.is_valid_timeout(timeout), "Timeout expected to be an integer"
19 | assert isinstance(fatal, bool), "Fatal expected a boolean value"
20 |
21 | self.error_message = str(error_message)
22 | self.error_is_fatal = fatal
23 |
24 | self.suggestion = suggestion
25 | self.create_exception_message(timeout)
26 |
27 | def create_exception_message(self, timeout=None):
28 | if timeout is not None:
29 | time.sleep(timeout)
30 |
31 | self.suggestion = self.create_suggestion_message(self.suggestion)
32 | self.throw_exception(self.error_message, self.suggestion)
33 |
34 | def throw_exception(self, error, suggestion):
35 | throw_exception_data = [
36 | colored.red(f"ERROR: {error}"),
37 | colored.green(suggestion),
38 | ]
39 | for element in throw_exception_data:
40 | print(element)
41 |
42 | if self.error_is_fatal:
43 | stop_current_application()
44 |
45 | def is_valid_timeout(self, timeout):
46 | return isinstance(timeout, int) or timeout == None
47 |
48 | def create_suggestion_message(self, suggestion):
49 | if suggestion == None:
50 | return None
51 |
52 | return f"HELP: {suggestion}"
53 |
54 | def __len__(self):
55 | return len(str(self.error_message))
56 |
57 | def __str__(self):
58 | return str(self.error_message)
59 |
60 | def __int__(self):
61 | return self.__len__()
62 |
63 | def __bool__(self):
64 | return bool(self.error_is_fatal)
65 |
--------------------------------------------------------------------------------
/polyglot/exceptions/exceptions.py:
--------------------------------------------------------------------------------
1 | from clint.textui import colored
2 |
3 |
4 | class PolyglotFileNotFoundError(FileNotFoundError):
5 | def __init__(self, message):
6 | self.message = colored.red(message)
7 |
8 | super().__init__(self.message)
9 |
10 | def str(self):
11 | return self.message
12 |
--------------------------------------------------------------------------------
/polyglot/ext/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | """
25 |
--------------------------------------------------------------------------------
/polyglot/ext/dir.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import prettytable
4 |
5 |
6 | class _Directory(object):
7 | def __init__(self, path=os.getcwd(), display=True):
8 | self.path = path
9 | self.show = display
10 |
11 | def create(self):
12 | data = self.__create_data()
13 | table = prettytable.PrettyTable()
14 | table.field_names = ["Modified at", "label", "size", "path"]
15 | for key in data:
16 | table.add_row(
17 | [
18 | data[key].get("modified"),
19 | data[key].get("label"),
20 | data[key].get("size"),
21 | key,
22 | ]
23 | )
24 |
25 | if self.show:
26 | print(table)
27 | return data
28 |
29 | def __create_data(self):
30 | data = {}
31 | files = os.listdir(self.path)
32 | data["."] = self.__generate_data(self.path)
33 | data[".."] = self.__generate_data(os.path.dirname(self.path))
34 |
35 | for file_index in range(len(files)):
36 | filename = files[file_index]
37 | data.setdefault(
38 | os.path.basename(filename),
39 | self.__generate_data(os.path.join(self.path, filename)),
40 | )
41 |
42 | return data
43 |
44 | def __generate_data(self, path):
45 | return {
46 | "modified": time.ctime(os.path.getmtime(path)),
47 | "label": "" if os.path.isdir(path) else "",
48 | "size": self.__get_file_length(path) if os.path.isfile(path) else "",
49 | }
50 |
51 | def __get_file_length(self, path):
52 | try:
53 | with open(path, "rb") as file_reader:
54 | return len(file_reader.read())
55 | except Exception as exception:
56 | return ""
57 |
58 |
59 | def directory(path=os.getcwd(), display=True):
60 | final_directory_path = path
61 | if final_directory_path == ".":
62 | final_directory_path = os.getcwd()
63 | elif final_directory_path == "..":
64 | final_directory_path = os.path.dirname(os.getcwd())
65 |
66 | return _Directory(final_directory_path, display).create()
67 |
68 |
69 | def ls(path=os.getcwd(), display=True):
70 | data = directory(path, display)
71 | return data
72 |
--------------------------------------------------------------------------------
/polyglot/ext/env.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from clint.textui import colored
4 |
5 |
6 | def find_token_type(value):
7 | if value.isdigit():
8 | return (int, float)
9 |
10 | return str
11 |
12 |
13 | class EnvAssignmentError(Exception):
14 | def __init__(self, error, line_number):
15 | self.message = error.strip()
16 | self.line = line_number
17 |
18 | error_message = colored.red(f"{self.message} at line {self.line}")
19 | super().__init__(error_message)
20 |
21 |
22 | class InvalidVariableName(EnvAssignmentError):
23 | def __init__(self, message, line):
24 | super().__init__(message, line)
25 |
26 |
27 | class Tokens(object):
28 | COMMENT_TOKEN = "#"
29 |
30 |
31 | class EnvironmentVariable(object):
32 | def __init__(self, variable, value, token_type, line_number):
33 | self.line = line_number
34 |
35 | self.variable = self.__check_variable_name(variable)
36 | self.value = value
37 | self.token_type = token_type
38 |
39 | def __check_variable_name(self, name):
40 | if len(name) == 0:
41 | raise InvalidVariableName(f"Invalid variable name {name}", self.line)
42 | first = name[0]
43 | if first.isdigit():
44 | raise InvalidVariableName(
45 | f"Variable name {name} starts with a number", self.line
46 | )
47 |
48 | return name
49 |
50 |
51 | class EnvParserPosition(object):
52 | def __init__(self, position=0):
53 | self.position = position
54 |
55 | def increment(self, increment_by=1):
56 | self.position += increment_by
57 |
58 | def decrement(self, decrement_by=1):
59 | self.position += -decrement_by
60 |
61 | def current_character(self, data):
62 | if len(data) == self.position:
63 | return None
64 |
65 | return data[self.position]
66 |
67 |
68 | class EnvParser(object):
69 | tokens = []
70 |
71 | def __init__(self, source, line):
72 | self.source = source.strip()
73 | self.line_number = line + 1
74 | self.position = EnvParserPosition(0)
75 | self.character = self.position.current_character(self.source)
76 |
77 | def create_parser_tokens(self):
78 | if len(self.source) == 0 or self.source.startswith(Tokens.COMMENT_TOKEN):
79 | return []
80 |
81 | assignment_counts = self.source.count("=")
82 | if assignment_counts > 1 or assignment_counts == 0:
83 | raise EnvAssignmentError("Multiple or no assignments ", self.line_number)
84 |
85 | name, value = self.source.split("=")
86 | token_type = find_token_type(value)
87 | existing_variables = list(
88 | filter(lambda list_element: list_element.variable == name, self.tokens)
89 | )
90 | if not len(existing_variables) == 0:
91 | raise InvalidVariableName(
92 | f"Duplicate variable name {name}", self.line_number
93 | )
94 | token = EnvironmentVariable(name, value, token_type, self.line_number)
95 | self.tokens.append(token)
96 |
97 | return self.tokens
98 |
99 | def update(self):
100 | self.position.increment(1)
101 | self.character = self.position.current_character(self.source)
102 |
103 |
104 |
105 | class Env(object):
106 | default_filename = os.path.join(os.getcwd(), ".env")
107 |
108 | def __init__(self, env=None, load=True, file=None):
109 | assert isinstance(env, str) or env == None, "Unexpected type of parameter env"
110 | self.env = env or os.path.join(os.path.dirname(file), ".env") or self.defualt_filename
111 | self.load_to_process = load
112 |
113 | def load(self):
114 | data = self.__read(self.env).split("\n")
115 | tokens = []
116 | for line_number in range(len(data)):
117 | parser = EnvParser(data[line_number], line_number)
118 | token_data = parser.create_parser_tokens()
119 | for token_element in token_data:
120 | tokens.append(token_element)
121 |
122 | for token_element in tokens:
123 | os.environ.setdefault(token_element.variable, token_element.value)
124 |
125 | def __read(self, filename):
126 | if not os.path.exists(filename) and os.path.isfile(filename):
127 | raise FileNotFoundError(f"{filename} does not exist")
128 |
129 | with open(filename, mode="r") as env_file_reader:
130 | return env_file_reader.read()
131 |
--------------------------------------------------------------------------------
/polyglot/ext/extensions.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 |
4 | class _FilterResults(object):
5 | def __init__(self, result, count=None):
6 | self.result = result
7 | self.count = count
8 |
9 | def __repr__(self):
10 | return f"Result:{self.result}"
11 |
12 |
13 | def __filter_extension_handler(file, extension):
14 | file_extension = f".{file.split('.')[-1:][0]}"
15 | return file.endswith(extension)
16 |
17 |
18 | def filter_by_extension(extension, directory=os.getcwd(), count=True):
19 | files = list(
20 | filter(
21 | lambda file: __filter_extension_handler(file, extension),
22 | os.listdir(directory),
23 | )
24 | )
25 | results = _FilterResults(files)
26 | if count:
27 | results.count = len(files)
28 | return results
29 |
--------------------------------------------------------------------------------
/polyglot/ext/json.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import uuid
4 |
5 | class UseAscii(object):
6 | @staticmethod
7 | def string_to_ascii_string(string, separator="."):
8 | assert "__str__" in dir(string)
9 | string_form, return_value = str(string), ""
10 | for current_character in string_form:
11 | return_value += str(UseAscii.get_ascii_value(current_character)) + separator
12 | return return_value
13 |
14 | @staticmethod
15 | def get_ascii_value(character):
16 | try:
17 | return ord(character)
18 | except Exception as exception:
19 | return -1
20 |
21 | @staticmethod
22 | def ascii_string_to_string(ascii_string, separator="."):
23 | return_value = ""
24 | for character in list(filter(lambda element: element.strip(),ascii_string.split(separator))):
25 | if len(character) == 0:continue
26 | value = int(character)
27 | if value == -1:continue
28 | return_value += chr(value)
29 | return return_value
30 |
31 | class JsonStore(object):
32 | def __init__(self, filename, database_name, typeof_data="any"):
33 | self.file = filename
34 | self.name = database_name.strip()
35 | self.__path = os.path.join(os.path.dirname(self.file), f"{self.name}.json")
36 |
37 | assert len(self.name) > 0, "Name should have atleast one character"
38 | if not (type(typeof_data).__name__ == "type" or typeof_data == "any"):
39 | raise TypeError(f"Invalid type : {type(typeof_data).__name__}")
40 |
41 | self.typeof_data = typeof_data
42 | self.__store = self.initialize_database()
43 |
44 | self.__store = self.__store if isinstance(self.__store, dict) else {}
45 |
46 | self.commit()
47 |
48 | @property
49 | def keys(self):
50 | return list(self.__store.keys())
51 |
52 | def __create_short_uuid(self, check_for_duplicate=[], length=5):
53 | short_uuid = lambda: str(uuid.uuid4())[:length]
54 | value = short_uuid()
55 | while value in check_for_duplicate:
56 | value = short_uuid()
57 | return value
58 |
59 | def initialize_database(self):
60 | if not os.path.isfile(self.__path):
61 | with open(self.__path, "w") as file_writer:
62 | file_writer.write(json.dumps({}))
63 | return {}
64 | return self.__get_store_content()
65 |
66 | def __get_store_content(self):
67 | with open(self.__path, "r") as file_reader:
68 | try:
69 | return json.loads(file_reader.read())
70 | except Exception as exception:
71 | with open(self.__path, "w") as file_writer:
72 | file_writer.write(json.dumps({}))
73 | return {}
74 |
75 | def add(self, data):
76 | if not self.__validate_data_type(data):
77 | raise TypeError(f"Parameter")
78 | key = self.__create_short_uuid(check_for_duplicate=self.keys)
79 | self.__store[key] = (
80 | UseAscii.string_to_ascii_string(data) if isinstance(data, str) else data
81 | )
82 | self.commit()
83 |
84 | @property
85 | def __expected_parameter_type(self):
86 | if self.typeof_data == "any":
87 | return "any"
88 |
89 | return self.typeof_data.__name__
90 |
91 | def filter_by_value(self, value):
92 | values = list(self.__store.values())
93 | keys = self.keys
94 | matches = [
95 | keys[index] if val == value else None for index, val in enumerate(values)
96 | ]
97 | return list(filter(lambda element: element is not None, matches))
98 |
99 | def get(self, key=None):
100 | if len(self.keys) == 0 and key == None:
101 | return None
102 | return_value = self.__store[key or self.keys[0]]
103 | if not isinstance(return_value, str):
104 | return return_value
105 | return UseAscii.ascii_string_to_string(return_value)
106 |
107 | def commit(self):
108 | with open(self.__path, "w") as file_writer:
109 | file_writer.write(json.dumps(self.__store))
110 |
111 | def __validate_data_type(self, data):
112 | if self.typeof_data == "any":
113 | return True
114 | return isinstance(data, self.typeof_data)
115 |
--------------------------------------------------------------------------------
/polyglot/path.py:
--------------------------------------------------------------------------------
1 | import os
2 | from clint.textui import colored
3 | from collections.abc import Iterable
4 |
5 |
6 | class DirectoryError(Exception):
7 | def __init__(self, error_message):
8 | self.error_message = colored.red(error_message)
9 |
10 | super().__init__(self.error_message)
11 |
12 |
13 | class FileContentFilter(object):
14 | def __init__(self, files=None, folders=None):
15 | self.__validate_parameter_types(files=files, folders=folders)
16 |
17 | self.files = files
18 | self.folders = folders
19 |
20 | def __validate_parameter_types(self, **kwargs):
21 | valid_types = bool
22 | for parameter_key, value in kwargs.items():
23 | if not type(value) == valid_types:
24 | if value == None:
25 | continue
26 | raise TypeError(f"{parameter_key} expected to be of type bool or None")
27 | return True
28 |
29 |
30 | class Log(object):
31 | def __init__(self, message, critical=False):
32 | self.message = colored.red(message) if critical else colored.cyan(message)
33 | self.create_message_log(self.message)
34 |
35 | def create_message_log(self, message, end="\n"):
36 | print(message, end=end)
37 |
38 |
39 | class _Stat(object):
40 | def __init__(self, path):
41 | self.path = path
42 |
43 | self.parent = os.path.dirname(path)
44 | self.basename = os.path.basename(path)
45 | self.directory = os.path.isdir(path)
46 | self.file = os.path.isfile(path)
47 | self.absolute = os.path.abspath(path)
48 |
49 | def __repr__(self):
50 | return str(
51 | {
52 | "parent": self.parent,
53 | "basename": self.basename,
54 | "directory": self.directory,
55 | "file": self.file,
56 | "abs": self.absolute,
57 | }
58 | )
59 |
60 | def __str__(self):
61 | return self.__repr__()
62 |
63 |
64 | class PolyglotPath(object):
65 | def __init__(self, path=None):
66 | self.directory = self.__find_directory_path(path)
67 |
68 | def __find_directory_path(self, path):
69 | if path == "." or path == None:
70 | return os.getcwd()
71 |
72 | return path
73 |
74 | @property
75 | def basename(self):
76 | return os.path.basename(self.directory)
77 |
78 | def listdir(self):
79 | return os.listdir(self.directory)
80 |
81 | @property
82 | def content(self):
83 | return self.listdir()
84 |
85 | @property
86 | def is_directory(self):
87 | return os.path.isdir(self.directory)
88 |
89 | def touch(self, create_files=[], log=True):
90 | assert isinstance(
91 | create_files, Iterable
92 | ), "Parameter expected to be an iterable"
93 |
94 | for index, filename in enumerate(create_files):
95 | with open(
96 | os.path.join(self.directory, filename), "w"
97 | ) as create_file_writer:
98 | create_file_writer.write("")
99 |
100 | log = Log(f"{index+1} Created {filename}")
101 |
102 | def mkdirs(self, directories, log=True, overwrite=False):
103 | assert isinstance(directories, Iterable)
104 |
105 | for index, dirname in enumerate(directories):
106 | if os.path.exists(dirname) and os.path.isdir(dirname):
107 | Log(f"{index+1} Failed to create {dirname}", critical=True)
108 | else:
109 | os.mkdir(dirname)
110 | Log(f"{index+1}. Created {dirname}")
111 |
112 | def join(self, *args):
113 | path = self.directory
114 | for joinpath in args:
115 | path = os.path.join(path, joinpath)
116 |
117 | return path
118 |
119 | @property
120 | def parent(self):
121 | return os.path.dirname(self.directory)
122 |
123 | @property
124 | def stat(self):
125 | return _Stat(self.directory)
126 |
127 | def __repr__(self):
128 | return str(self.directory)
129 |
130 | def __str__(self):
131 | return self.__repr__()
132 |
133 | def __len__(self):
134 | if not os.path.isdir(self.directory):
135 | return -1
136 |
137 | return len(os.listdir(self.directory))
138 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 | pyyaml
3 | prettytable
4 | clint
5 | toml
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open("README.md", "r", encoding="utf-8") as fh:
4 | long_description = fh.read()
5 |
6 | DEPENDENCIES = ["requests", "pyyaml", "prettytable", "clint", "toml"]
7 |
8 |
9 | setuptools.setup(
10 | name="python-polyglot", # Replace with your own username
11 | version="4.2.9",
12 | author="P Pranav Baburaj",
13 | author_email="code-roller@googlegroups.com",
14 | description="Find the percentage of programming languages used in your project",
15 | long_description=long_description,
16 | long_description_content_type="text/markdown",
17 | url="https://github.com/pranavbaburaj/polyglot",
18 | packages=setuptools.find_packages(
19 | exclude=["tests", "*.tests", "*.tests.*", "tests.*"]
20 | ),
21 | install_requires=DEPENDENCIES,
22 | classifiers=[
23 | "Programming Language :: Python :: 3",
24 | "License :: OSI Approved :: MIT License",
25 | "Operating System :: OS Independent",
26 | ],
27 | entry_points={
28 | "console_scripts": [
29 | "pgt = polyglot.__main__:main",
30 | "polyglot = polyglot.__main__:main",
31 | ]
32 | },
33 | python_requires=">=3.6",
34 | )
35 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | from polyglot.core import Polyglot
2 |
3 | poly = Polyglot("E://clones//v//").show()
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | MIT License
3 |
4 | Copyright (c) 2021 Pranav Baburaj
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | """
24 |
--------------------------------------------------------------------------------
/tests/context.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 |
4 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
5 |
6 | import polyglot
7 |
8 | from polyglot.core import *
9 | from polyglot.exceptions import *
10 | from polyglot.arguments import *
11 | from polyglot.path import PolyglotPath
12 |
--------------------------------------------------------------------------------
/tests/test_arguments.py:
--------------------------------------------------------------------------------
1 | from context import Arguments
2 |
3 | import unittest
4 |
5 |
6 | class TestCases(unittest.TestCase):
7 | """Advanced test cases."""
8 |
9 | def test_application(self):
10 | data = Arguments(
11 | arguments=[
12 | "--dir=.",
13 | "--o=dara.json",
14 | "--show=True",
15 | "--ignore=data.json,file.json,test.json",
16 | ]
17 | )
18 |
19 | print(data.parse())
20 | self.assertIsNone(None)
21 |
22 |
23 | if __name__ == "__main__":
24 | unittest.main()
25 |
--------------------------------------------------------------------------------
/tests/test_file.py:
--------------------------------------------------------------------------------
1 | from context import PolyglotPath
2 |
3 | import unittest
4 |
5 |
6 | class TestCases(unittest.TestCase):
7 | """Advanced test cases."""
8 |
9 | def test_application(self):
10 | d = PolyglotPath(".")
11 | print(d.listdir())
12 | self.assertIsNone(None)
13 |
14 |
15 | if __name__ == "__main__":
16 | unittest.main()
17 |
--------------------------------------------------------------------------------
/tests/test_polyglot.py:
--------------------------------------------------------------------------------
1 | from context import polyglot
2 |
3 | import unittest
4 |
5 |
6 | class TestCases(unittest.TestCase):
7 | """Advanced test cases."""
8 |
9 | def test_application(self):
10 | self.assertIsNone(None)
11 |
12 |
13 | if __name__ == "__main__":
14 | unittest.main()
15 |
--------------------------------------------------------------------------------