youtrack-python 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. youtrack_python-0.1.1/.coveragerc +9 -0
  2. youtrack_python-0.1.1/.github/workflows/lint-and-test.yml +68 -0
  3. youtrack_python-0.1.1/.github/workflows/release.yml +90 -0
  4. youtrack_python-0.1.1/.gitignore +8 -0
  5. youtrack_python-0.1.1/.idea/watcherTasks.xml +46 -0
  6. youtrack_python-0.1.1/.run/Run Tests.run.xml +21 -0
  7. youtrack_python-0.1.1/LICENSE +22 -0
  8. youtrack_python-0.1.1/PKG-INFO +149 -0
  9. youtrack_python-0.1.1/README.md +107 -0
  10. youtrack_python-0.1.1/examples/list_project_custom_fields.py +274 -0
  11. youtrack_python-0.1.1/pyproject.toml +57 -0
  12. youtrack_python-0.1.1/pyrightconfig.json +15 -0
  13. youtrack_python-0.1.1/ruff.toml +20 -0
  14. youtrack_python-0.1.1/scripts/check.py +55 -0
  15. youtrack_python-0.1.1/tests/__init__.py +0 -0
  16. youtrack_python-0.1.1/tests/responses/agile.json +44 -0
  17. youtrack_python-0.1.1/tests/responses/agiles.json +86 -0
  18. youtrack_python-0.1.1/tests/responses/issue.json +200 -0
  19. youtrack_python-0.1.1/tests/responses/issue_comments.json +64 -0
  20. youtrack_python-0.1.1/tests/responses/issue_link_types.json +54 -0
  21. youtrack_python-0.1.1/tests/responses/issue_links.json +214 -0
  22. youtrack_python-0.1.1/tests/responses/issue_work_items.json +38 -0
  23. youtrack_python-0.1.1/tests/responses/issues.json +389 -0
  24. youtrack_python-0.1.1/tests/responses/issues_custom_model.json +102 -0
  25. youtrack_python-0.1.1/tests/responses/project_custom_fields.json +159 -0
  26. youtrack_python-0.1.1/tests/responses/projects.json +14 -0
  27. youtrack_python-0.1.1/tests/responses/sprint.json +18 -0
  28. youtrack_python-0.1.1/tests/responses/sprints.json +239 -0
  29. youtrack_python-0.1.1/tests/responses/tags.json +17 -0
  30. youtrack_python-0.1.1/tests/responses/users.json +31 -0
  31. youtrack_python-0.1.1/tests/responses/work_item_types.json +17 -0
  32. youtrack_python-0.1.1/tests/test_async_client.py +878 -0
  33. youtrack_python-0.1.1/tests/test_client.py +842 -0
  34. youtrack_python-0.1.1/tests/test_converters.py +169 -0
  35. youtrack_python-0.1.1/tests/test_definitions.py +545 -0
  36. youtrack_python-0.1.1/tests/test_helpers.py +63 -0
  37. youtrack_python-0.1.1/tests/test_types.py +148 -0
  38. youtrack_python-0.1.1/uv.lock +501 -0
  39. youtrack_python-0.1.1/youtrack_sdk/__init__.py +4 -0
  40. youtrack_python-0.1.1/youtrack_sdk/async_client.py +712 -0
  41. youtrack_python-0.1.1/youtrack_sdk/base_client.py +75 -0
  42. youtrack_python-0.1.1/youtrack_sdk/client.py +712 -0
  43. youtrack_python-0.1.1/youtrack_sdk/entities.py +907 -0
  44. youtrack_python-0.1.1/youtrack_sdk/exceptions.py +14 -0
  45. youtrack_python-0.1.1/youtrack_sdk/helpers.py +152 -0
  46. youtrack_python-0.1.1/youtrack_sdk/py.typed +1 -0
  47. youtrack_python-0.1.1/youtrack_sdk/types.py +54 -0
@@ -0,0 +1,9 @@
1
+ [run]
2
+ branch = True
3
+ relative_files = True
4
+
5
+ [report]
6
+ omit = tests/*
7
+ show_missing = True
8
+ skip_covered = False
9
+ precision = 2
@@ -0,0 +1,68 @@
1
+ name: Lint and Test (ruff + pyright + pytest)
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ lint-and-test:
10
+ name: Lint and Test
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Check out code
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v6
19
+ with:
20
+ enable-cache: true
21
+ cache-dependency-glob: |
22
+ **/pyproject.toml
23
+ **/uv.lock
24
+
25
+ - name: Show uv version
26
+ run: uv --version
27
+
28
+ - name: Sync dependencies (frozen)
29
+ run: uv sync --frozen
30
+
31
+ - name: Check formatting (ruff)
32
+ run: uv run ruff format --check
33
+
34
+ - name: Lint (ruff)
35
+ run: uv run ruff check
36
+
37
+ - name: Static type checking
38
+ run: uv run pyright
39
+
40
+ - name: Tests with coverage (pytest-cov)
41
+ run: |
42
+ uv run \
43
+ pytest tests/ \
44
+ --cov=youtrack_sdk \
45
+ --cov-branch \
46
+ --cov-config="${{ github.workspace }}/.coveragerc" \
47
+ --cov-report=term-missing:skip-covered \
48
+ --cov-report=xml:coverage.xml \
49
+ --cov-report=html:coverage_html \
50
+ --cov-fail-under=85
51
+
52
+ - name: Add coverage summary
53
+ run: |
54
+ python3 - <<'PY' | tee -a "$GITHUB_STEP_SUMMARY"
55
+ import xml.etree.ElementTree as ET
56
+ r = float(ET.parse('coverage.xml').getroot().get('branch-rate'))*100
57
+ print(f"coverage: {r:.2f}%")
58
+ PY
59
+
60
+ - name: Upload coverage artifacts
61
+ uses: actions/upload-artifact@v4
62
+ with:
63
+ name: coverage-reports
64
+ path: |
65
+ coverage.xml
66
+ coverage_html
67
+ if-no-files-found: ignore
68
+ retention-days: 14
@@ -0,0 +1,90 @@
1
+ name: Release (build + publish to PyPI)
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ verify-version:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ steps:
13
+ - name: Check out tag
14
+ uses: actions/checkout@v4
15
+ with:
16
+ ref: ${{ github.event.release.tag_name }}
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.13"
22
+
23
+ - name: Verify tag matches pyproject.toml version
24
+ env:
25
+ TAG_NAME: ${{ github.event.release.tag_name }}
26
+ run: |
27
+ python - <<'PY'
28
+ import os, re, sys, pathlib, tomllib
29
+
30
+ tag = os.environ["TAG_NAME"]
31
+ m = re.fullmatch(r"v(\d+\.\d+\.\d+)", tag)
32
+ if not m:
33
+ print(f"ERROR: Release tag {tag!r} is not in vX.Y.Z format.")
34
+ sys.exit(1)
35
+
36
+ data = tomllib.loads(pathlib.Path("pyproject.toml").read_text("utf-8"))
37
+ version = data.get("project", {}).get("version")
38
+ if not version:
39
+ print("ERROR: Missing [project].version in pyproject.toml.")
40
+ sys.exit(1)
41
+
42
+ tag_version = m.group(1)
43
+ if version != tag_version:
44
+ print(f"ERROR: Version mismatch: pyproject.toml={version!r} tag={tag_version!r}")
45
+ sys.exit(1)
46
+
47
+ print(f"OK: {version} matches {tag}")
48
+ PY
49
+
50
+ build:
51
+ runs-on: ubuntu-latest
52
+ needs: [verify-version]
53
+ permissions:
54
+ contents: read
55
+ steps:
56
+ - name: Check out tag
57
+ uses: actions/checkout@v4
58
+ with:
59
+ ref: ${{ github.event.release.tag_name }}
60
+
61
+ - name: Install uv
62
+ uses: astral-sh/setup-uv@v3
63
+
64
+ - name: Build distributions
65
+ run: uv build
66
+
67
+ - name: Upload dist artifacts
68
+ uses: actions/upload-artifact@v4
69
+ with:
70
+ name: dist
71
+ path: dist/*
72
+
73
+ publish:
74
+ runs-on: ubuntu-latest
75
+ needs: [build]
76
+ environment:
77
+ name: pypi
78
+ url: https://pypi.org/p/youtrack-python
79
+ permissions:
80
+ id-token: write
81
+ contents: read
82
+ steps:
83
+ - name: Download dist artifacts
84
+ uses: actions/download-artifact@v4
85
+ with:
86
+ name: dist
87
+ path: dist
88
+
89
+ - name: Publish to PyPI (Trusted Publishing)
90
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+
4
+ /.idea/*
5
+ !/.idea/watcherTasks.xml
6
+ .vscode/
7
+ coverage_html/
8
+ **/.coverage
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectTasksOptions">
4
+ <TaskOptions isEnabled="true">
5
+ <option name="arguments" value="sh -c &quot;$PyInterpreterDirectory$/ruff check --fix $FilePath$ ; $PyInterpreterDirectory$/ruff format $FilePath$ ; $PyInterpreterDirectory$/ruff check --fix $FilePath$ ; $PyInterpreterDirectory$/ruff format $FilePath$&quot;" />
6
+ <option name="checkSyntaxErrors" value="true" />
7
+ <option name="description" />
8
+ <option name="exitCodeBehavior" value="NEVER" />
9
+ <option name="fileExtension" value="py" />
10
+ <option name="immediateSync" value="false" />
11
+ <option name="name" value="ruff" />
12
+ <option name="output" value="$FilePath$" />
13
+ <option name="outputFilters">
14
+ <array />
15
+ </option>
16
+ <option name="outputFromStdout" value="false" />
17
+ <option name="program" value="env" />
18
+ <option name="runOnExternalChanges" value="true" />
19
+ <option name="scopeName" value="Project Files" />
20
+ <option name="trackOnlyRoot" value="false" />
21
+ <option name="workingDir" value="$ProjectFileDir$" />
22
+ <envs />
23
+ </TaskOptions>
24
+
25
+ <TaskOptions isEnabled="true">
26
+ <option name="arguments" value="-m json.tool --indent 2 --no-ensure-ascii $FilePath$ $FilePath$" />
27
+ <option name="checkSyntaxErrors" value="true" />
28
+ <option name="description" />
29
+ <option name="exitCodeBehavior" value="ERROR" />
30
+ <option name="fileExtension" value="json" />
31
+ <option name="immediateSync" value="false" />
32
+ <option name="name" value="JSON formatting" />
33
+ <option name="output" value="$FilePath$" />
34
+ <option name="outputFilters">
35
+ <array />
36
+ </option>
37
+ <option name="outputFromStdout" value="false" />
38
+ <option name="program" value="$PyInterpreterDirectory$/python" />
39
+ <option name="runOnExternalChanges" value="true" />
40
+ <option name="scopeName" value="All Changed Files" />
41
+ <option name="trackOnlyRoot" value="false" />
42
+ <option name="workingDir" value="$ProjectFileDir$" />
43
+ <envs />
44
+ </TaskOptions>
45
+ </component>
46
+ </project>
@@ -0,0 +1,21 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="Run Tests" type="tests" factoryName="py.test">
3
+ <module name="youtrack-sdk" />
4
+ <option name="ENV_FILES" value="" />
5
+ <option name="INTERPRETER_OPTIONS" value="" />
6
+ <option name="PARENT_ENVS" value="true" />
7
+ <option name="SDK_HOME" value="" />
8
+ <option name="WORKING_DIRECTORY" value="" />
9
+ <option name="IS_MODULE_SDK" value="true" />
10
+ <option name="ADD_CONTENT_ROOTS" value="true" />
11
+ <option name="ADD_SOURCE_ROOTS" value="true" />
12
+ <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
13
+ <option name="RUN_TOOL" value="true" />
14
+ <option name="_new_keywords" value="&quot;&quot;" />
15
+ <option name="_new_parameters" value="&quot;&quot;" />
16
+ <option name="_new_additionalArguments" value="&quot;&quot;" />
17
+ <option name="_new_target" value="&quot;$PROJECT_DIR$/tests&quot;" />
18
+ <option name="_new_targetType" value="&quot;PATH&quot;" />
19
+ <method v="2" />
20
+ </configuration>
21
+ </component>
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2024 moneymeets GmbH
4
+ Copyright (c) 2026 Ninjaneers GmbH
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.
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: youtrack-python
3
+ Version: 0.1.1
4
+ Summary: YouTrack SDK
5
+ Project-URL: Repository, https://github.com/ninjaneers-team/youtrack-sdk
6
+ Project-URL: Homepage, https://github.com/moneymeets/youtrack-sdk
7
+ Author-email: moneymeets <service@moneymeets.com>, Michael Gerhold <michael.gerhold@ninjaneers.de>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2023-2024 moneymeets GmbH
11
+ Copyright (c) 2026 Ninjaneers GmbH
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: sdk,youtrack
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
+ Requires-Python: >=3.13
39
+ Requires-Dist: httpx>=0.28.1
40
+ Requires-Dist: pydantic>=2.12.5
41
+ Description-Content-Type: text/markdown
42
+
43
+ # YouTrack REST API Client
44
+
45
+ A client library for accessing YouTrack REST API.
46
+
47
+ This is a fork of [moneymeets/youtrack-sdk](https://github.com/moneymeets/youtrack-sdk) with several enhancements.
48
+
49
+ ## Key Changes from Original
50
+
51
+ This fork includes the following improvements over the original repository:
52
+
53
+ - **AsyncClient Implementation**: Full async/await support with `AsyncClient` for non-blocking I/O operations
54
+ - **Project Custom Fields API**: New `get_project_custom_fields()` method to retrieve custom field definitions for projects
55
+ - **Migrated to UV**: Switched from Poetry to UV for dependency management
56
+ - **Enhanced Testing**: Migrated test suite from unittest to Pytest with async test support
57
+ - **Modern Python**: Updated to require Python 3.13+ with improved type hints and linting
58
+ - **Improved Code Quality**: Enhanced linting with Ruff and type checking with Pyright
59
+
60
+ ## Usage
61
+
62
+ ### Synchronous Client
63
+
64
+ ```python
65
+ from datetime import date
66
+ from youtrack_sdk import Client
67
+ from youtrack_sdk.entities import (
68
+ DateIssueCustomField,
69
+ EnumBundleElement,
70
+ Issue,
71
+ Tag,
72
+ Project,
73
+ SingleEnumIssueCustomField,
74
+ SingleUserIssueCustomField,
75
+ StateBundleElement,
76
+ StateIssueCustomField,
77
+ User,
78
+ )
79
+
80
+ with Client(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
81
+ result = client.create_issue(
82
+ issue=Issue(
83
+ project=Project(id="0-0"),
84
+ summary="Created from YouTrack SDK",
85
+ description="Description **text**.",
86
+ tags=[
87
+ Tag(id="6-0"),
88
+ ],
89
+ custom_fields=[
90
+ StateIssueCustomField(
91
+ name="State",
92
+ value=StateBundleElement(
93
+ name="In Progress",
94
+ ),
95
+ ),
96
+ SingleUserIssueCustomField(
97
+ name="Assignee",
98
+ value=User(
99
+ ring_id="00000000-a31c-4174-bb27-abd3387df67a",
100
+ ),
101
+ ),
102
+ SingleEnumIssueCustomField(
103
+ name="Type",
104
+ value=EnumBundleElement(
105
+ name="Bug",
106
+ ),
107
+ ),
108
+ DateIssueCustomField(
109
+ name="Due Date",
110
+ value=date(2005, 12, 31),
111
+ ),
112
+ ],
113
+ ),
114
+ )
115
+ ```
116
+
117
+ ### Async Client
118
+
119
+ ```python
120
+ import asyncio
121
+ from youtrack_sdk import AsyncClient
122
+ from youtrack_sdk.entities import Project
123
+
124
+ async def main():
125
+ async with AsyncClient(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
126
+ projects = await client.get_projects()
127
+ for project in projects:
128
+ print(f"Project: {project.name}")
129
+
130
+ asyncio.run(main())
131
+ ```
132
+
133
+ ### Fetching Project Custom Fields
134
+
135
+ ```python
136
+ from youtrack_sdk import Client
137
+
138
+ with Client(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
139
+ custom_fields = client.get_project_custom_fields(project_id="0-0")
140
+
141
+ for field in custom_fields:
142
+ print(f"Field: {field.field.name}, Type: {type(field).__name__}")
143
+ ```
144
+
145
+ See [examples/list_project_custom_fields.py](examples/list_project_custom_fields.py) for a complete example.
146
+
147
+ ## Note
148
+
149
+ - You should prefer to use internal entity IDs everywhere. Some methods accept readable issue IDs (e.g. HD-99) but it's not supported everywhere.
@@ -0,0 +1,107 @@
1
+ # YouTrack REST API Client
2
+
3
+ A client library for accessing YouTrack REST API.
4
+
5
+ This is a fork of [moneymeets/youtrack-sdk](https://github.com/moneymeets/youtrack-sdk) with several enhancements.
6
+
7
+ ## Key Changes from Original
8
+
9
+ This fork includes the following improvements over the original repository:
10
+
11
+ - **AsyncClient Implementation**: Full async/await support with `AsyncClient` for non-blocking I/O operations
12
+ - **Project Custom Fields API**: New `get_project_custom_fields()` method to retrieve custom field definitions for projects
13
+ - **Migrated to UV**: Switched from Poetry to UV for dependency management
14
+ - **Enhanced Testing**: Migrated test suite from unittest to Pytest with async test support
15
+ - **Modern Python**: Updated to require Python 3.13+ with improved type hints and linting
16
+ - **Improved Code Quality**: Enhanced linting with Ruff and type checking with Pyright
17
+
18
+ ## Usage
19
+
20
+ ### Synchronous Client
21
+
22
+ ```python
23
+ from datetime import date
24
+ from youtrack_sdk import Client
25
+ from youtrack_sdk.entities import (
26
+ DateIssueCustomField,
27
+ EnumBundleElement,
28
+ Issue,
29
+ Tag,
30
+ Project,
31
+ SingleEnumIssueCustomField,
32
+ SingleUserIssueCustomField,
33
+ StateBundleElement,
34
+ StateIssueCustomField,
35
+ User,
36
+ )
37
+
38
+ with Client(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
39
+ result = client.create_issue(
40
+ issue=Issue(
41
+ project=Project(id="0-0"),
42
+ summary="Created from YouTrack SDK",
43
+ description="Description **text**.",
44
+ tags=[
45
+ Tag(id="6-0"),
46
+ ],
47
+ custom_fields=[
48
+ StateIssueCustomField(
49
+ name="State",
50
+ value=StateBundleElement(
51
+ name="In Progress",
52
+ ),
53
+ ),
54
+ SingleUserIssueCustomField(
55
+ name="Assignee",
56
+ value=User(
57
+ ring_id="00000000-a31c-4174-bb27-abd3387df67a",
58
+ ),
59
+ ),
60
+ SingleEnumIssueCustomField(
61
+ name="Type",
62
+ value=EnumBundleElement(
63
+ name="Bug",
64
+ ),
65
+ ),
66
+ DateIssueCustomField(
67
+ name="Due Date",
68
+ value=date(2005, 12, 31),
69
+ ),
70
+ ],
71
+ ),
72
+ )
73
+ ```
74
+
75
+ ### Async Client
76
+
77
+ ```python
78
+ import asyncio
79
+ from youtrack_sdk import AsyncClient
80
+ from youtrack_sdk.entities import Project
81
+
82
+ async def main():
83
+ async with AsyncClient(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
84
+ projects = await client.get_projects()
85
+ for project in projects:
86
+ print(f"Project: {project.name}")
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ### Fetching Project Custom Fields
92
+
93
+ ```python
94
+ from youtrack_sdk import Client
95
+
96
+ with Client(base_url="https://dummy.myjetbrains.com/youtrack", token="dummy") as client:
97
+ custom_fields = client.get_project_custom_fields(project_id="0-0")
98
+
99
+ for field in custom_fields:
100
+ print(f"Field: {field.field.name}, Type: {type(field).__name__}")
101
+ ```
102
+
103
+ See [examples/list_project_custom_fields.py](examples/list_project_custom_fields.py) for a complete example.
104
+
105
+ ## Note
106
+
107
+ - You should prefer to use internal entity IDs everywhere. Some methods accept readable issue IDs (e.g. HD-99) but it's not supported everywhere.