pygitpub 0.0.58__tar.gz → 0.0.59__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.
@@ -4,11 +4,16 @@
4
4
  /dist/
5
5
  /target/
6
6
  /out/
7
+ /debug/
8
+ /download/
7
9
  *.out
8
10
  /.mypy_cache/
11
+ /.ruff_cache/
12
+ /.rumdl_cache/
9
13
 
10
14
  # folders to git ignore
11
15
  /*.gi
16
+ /gi/
12
17
 
13
18
  # gems
14
19
  /gems/
@@ -33,6 +38,8 @@ lib*.so.*
33
38
  *.gch
34
39
  *.pchi
35
40
  *.gcda
41
+ *.map
42
+ *.bin
36
43
 
37
44
  # kernel stuff
38
45
  *.ko
@@ -59,6 +66,9 @@ modules.order
59
66
  /docs/_build/
60
67
  /docs/.doctrees/environment.pickle
61
68
 
69
+ # mdBook build output
70
+ /docs/book/
71
+
62
72
  # installations
63
73
  /external/
64
74
  /install/
@@ -67,6 +77,8 @@ modules.order
67
77
  # python
68
78
  __pycache__/
69
79
  *.py[oc]
80
+ *.pyc
81
+ *.pyo
70
82
  /wheel/
71
83
  /.venv/
72
84
  .coverage
@@ -110,11 +122,15 @@ node_modules/
110
122
  # haskell
111
123
  *.hi
112
124
 
125
+ # clojure
126
+ .cpcache/
127
+
113
128
  # repositries of deb packages
114
129
  /deb/mypackage/DEBIAN/control
115
130
 
116
131
  # generic
117
132
  *.log
133
+ *.swp
118
134
 
119
135
  # scons
120
136
  .sconsign.dblite
@@ -133,6 +149,16 @@ build/
133
149
  # c#
134
150
  **/obj/
135
151
  **/bin/
152
+ [Oo]bj/
153
+ [Bb]in/
154
+
155
+ # flash/air
156
+ *.swf
157
+ *.air
158
+ *.apk
159
+ *.ipa
160
+ bin-debug/
161
+ bin-release/
136
162
 
137
163
  # vscode
138
164
  .vscode/
@@ -144,10 +170,12 @@ build/
144
170
  # jetbrains and eclipse
145
171
  .cache
146
172
  .settings
173
+ .settings/
147
174
  .classpath
148
175
  .project
149
176
  .project.xml
150
177
  .idea/
178
+ /.idea/
151
179
  build.properties
152
180
 
153
181
  # spark
@@ -155,9 +183,62 @@ spark-warehouse/
155
183
 
156
184
  # terraform
157
185
  .terraform*
186
+ /.terraform/
158
187
  terraform.tfstate
159
188
  terraform.tfstate.backup
189
+ /terraform.tfstate
190
+ /terraform.tfstate.backup
191
+ /terraform.lock.hcl
160
192
 
161
193
  # scala
162
194
  target/
163
195
  project/
196
+
197
+ # rust
198
+ **/*.rs.bk
199
+ **/mutants.out*/
200
+ *.pdb
201
+ debug
202
+ /.rsconstruct_cache.json
203
+
204
+ # claude
205
+ /.claude/
206
+ claude/
207
+ .claude/projects/
208
+ .claude/settings.local.json
209
+
210
+ # static sites
211
+ /_site/
212
+ /public/
213
+ /media/
214
+ /static/build_info.toml
215
+ /static/shared-themes/
216
+ /build_info.json
217
+
218
+ # manim
219
+ /.manim_scratch/
220
+
221
+ # terms
222
+ /terms.ambiguous/
223
+ /terms.single_meaning/
224
+
225
+ # stm32 demos
226
+ /STM32CubeWL/
227
+ /exercises/*/*/.obj/
228
+ /exercises/*/*/app*.elf
229
+ /exercises/*/*/app*.map
230
+ /exercises/*/*/firmware*.bin
231
+ /exercises/common/.hal_obj_cm0/
232
+ /exercises/common/.hal_obj_cm4/
233
+ /exercises/common/.rsc_hal_obj_cm0/
234
+ /exercises/common/.rsc_hal_obj_cm4/
235
+ /exercises/common/libhal_cm0.a
236
+ /exercises/common/libhal_cm4.a
237
+ /exercises/common/libhal_rsc_cm0.a
238
+ /exercises/common/libhal_rsc_cm4.a
239
+
240
+ # secrets
241
+ creds.txt
242
+
243
+ # rsconstruct
244
+ /.rsconstruct/
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Mark Veltzer
3
+ Copyright (c) 2026 Mark Veltzer <mark.veltzer@gmail.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pygitpub
3
- Version: 0.0.58
3
+ Version: 0.0.59
4
4
  Summary: Pygitpub will help you work with github
5
5
  Project-URL: Homepage, https://github.com/veltzer/pygitpub
6
6
  Project-URL: Bug Tracker, https://github.com/veltzer/pygitpub/issues
@@ -37,7 +37,7 @@ project website: https://veltzer.github.io/pygitpub
37
37
 
38
38
  author: Mark Veltzer
39
39
 
40
- version: 0.0.58
40
+ version: 0.0.59
41
41
 
42
42
  ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)
43
43
 
@@ -63,12 +63,11 @@ version: 0.0.58
63
63
  ![PyPI - Downloads](https://img.shields.io/pypi/dw/pygitpub)
64
64
  ![PyPI - Downloads](https://img.shields.io/pypi/dm/pygitpub)
65
65
 
66
-
67
-
68
66
  ## contact me
67
+
69
68
  [mailto](mailto:mark.veltzer@gmail.com)
70
69
  ![gitter](https://img.shields.io/gitter/room/veltzer/mark.veltzer)
71
70
  ![discord](https://img.shields.io/discord/719336281624281119)
72
71
  ![discord](https://img.shields.io/discord/719336282194444302)
73
72
 
74
- Mark Veltzer, Copyright © 2022, 2023, 2024, 2025
73
+ Mark Veltzer, Copyright © 2022, 2023, 2024, 2025, 2026
@@ -6,7 +6,7 @@ project website: https://veltzer.github.io/pygitpub
6
6
 
7
7
  author: Mark Veltzer
8
8
 
9
- version: 0.0.58
9
+ version: 0.0.59
10
10
 
11
11
  ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)
12
12
 
@@ -32,12 +32,11 @@ version: 0.0.58
32
32
  ![PyPI - Downloads](https://img.shields.io/pypi/dw/pygitpub)
33
33
  ![PyPI - Downloads](https://img.shields.io/pypi/dm/pygitpub)
34
34
 
35
-
36
-
37
35
  ## contact me
36
+
38
37
  [mailto](mailto:mark.veltzer@gmail.com)
39
38
  ![gitter](https://img.shields.io/gitter/room/veltzer/mark.veltzer)
40
39
  ![discord](https://img.shields.io/discord/719336281624281119)
41
40
  ![discord](https://img.shields.io/discord/719336282194444302)
42
41
 
43
- Mark Veltzer, Copyright © 2022, 2023, 2024, 2025
42
+ Mark Veltzer, Copyright © 2022, 2023, 2024, 2025, 2026
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pygitpub"
7
- version = "0.0.58"
7
+ version = "0.0.59"
8
8
  requires-python = ">=3.12"
9
9
  authors = [
10
10
  { name = "Mark Veltzer", email = "mark.veltzer@gmail.com" }
@@ -3,6 +3,14 @@ All configurations for pygitpub
3
3
  """
4
4
  from pytconf import Config, ParamCreator
5
5
 
6
+ # The values github accepts in the "affiliation" parameter of its
7
+ # "list repositories" api. They are sent over the wire verbatim.
8
+ AFFILIATIONS = [
9
+ "owner",
10
+ "collaborator",
11
+ "organization_member",
12
+ ]
13
+
6
14
 
7
15
  class ConfigGithub(Config):
8
16
  """
@@ -40,10 +48,19 @@ class ConfigAlgo(Config):
40
48
  help_string="Only include repos owned by this owner (None for dont mind)",
41
49
  default=None,
42
50
  )
51
+ affiliation = ParamCreator.create_str(
52
+ help_string="Comma separated list of how you are affiliated with the repos to work on"
53
+ f" (any of {','.join(AFFILIATIONS)})",
54
+ default="owner",
55
+ )
43
56
  show_extra = ParamCreator.create_bool(
44
57
  help_string="Show extra git repos lying around?",
45
58
  default=True,
46
59
  )
60
+ base_dir = ParamCreator.create_str(
61
+ help_string="Base directory under which repos are cloned",
62
+ default="~/git",
63
+ )
47
64
 
48
65
 
49
66
  class ConfigOutput(Config):
@@ -2,26 +2,51 @@
2
2
  main entry point to the program
3
3
  """
4
4
 
5
+ import json
5
6
  import os
6
7
  import subprocess
7
8
  import sys
8
- import json
9
-
10
- import pylogconf.core
11
- from pytconf import register_main, config_arg_parse_and_launch, register_endpoint
12
9
 
13
10
  import github
14
11
  import pyapikey
15
- from pygitpub.configs import ConfigGithub, ConfigOutput, ConfigAlgo
12
+ import pylogconf.core
13
+ from pytconf import config_arg_parse_and_launch, register_endpoint, register_main
14
+ from pytconf.pydoc import get_first_line
15
+ from pytconf.registry import the_registry
16
+
16
17
  import pygitpub.static
17
- from pygitpub.utils.misc import delete, get_all_git_repos
18
+ from pygitpub.configs import AFFILIATIONS, ConfigAlgo, ConfigGithub, ConfigOutput
18
19
  from pygitpub.utils.importlib import import_file
20
+ from pygitpub.utils.misc import delete, get_all_git_repos
21
+
22
+
23
+ def get_base_dir() -> str:
24
+ return os.path.expanduser(ConfigAlgo.base_dir)
25
+
26
+
27
+ def get_repo_folder(repo) -> str:
28
+ return os.path.join(get_base_dir(), repo.name)
29
+
30
+
31
+ def get_affiliation() -> str:
32
+ """ validate the affiliation parameter and return it in the form github wants """
33
+ values = [x.strip() for x in ConfigAlgo.affiliation.split(",")]
34
+ values = [x for x in values if x != ""]
35
+ if not values:
36
+ raise ValueError("affiliation must not be empty")
37
+ for value in values:
38
+ if value not in AFFILIATIONS:
39
+ raise ValueError(f"bad affiliation [{value}], must be one of [{','.join(AFFILIATIONS)}]")
40
+ return ",".join(values)
19
41
 
20
42
 
21
43
  def yield_repos():
22
44
  apikey = pyapikey.get_key(ConfigGithub.apikey)
23
45
  g = github.Github(login_or_token=apikey)
24
- for repo in g.get_user().get_repos():
46
+ # github filters by affiliation server side, so repos we are not interested
47
+ # in (org repos, by default) are never fetched in the first place
48
+ affiliation = get_affiliation()
49
+ for repo in g.get_user().get_repos(affiliation=affiliation):
25
50
  reason = "no reason set"
26
51
  skip = False
27
52
  if not ConfigAlgo.fork and repo.fork:
@@ -53,8 +78,7 @@ def yield_repos():
53
78
  def fix_metadata() -> None:
54
79
  orig_folder = os.getcwd()
55
80
  for repo in yield_repos():
56
- owner = repo.owner.login
57
- folder = os.path.join(owner, repo.name)
81
+ folder = get_repo_folder(repo)
58
82
  if not os.path.isdir(folder):
59
83
  # print(f"folder [{folder}] does not exist, continuing...")
60
84
  continue
@@ -68,7 +92,7 @@ def fix_metadata() -> None:
68
92
  if not hasattr(mod, "description_short"):
69
93
  os.chdir(orig_folder)
70
94
  continue
71
- description_short = getattr(mod, "description_short")
95
+ description_short = mod.description_short
72
96
  if description_short != repo.description:
73
97
  print(f"in {folder}...")
74
98
  print(f"description_short is [{description_short}]")
@@ -78,7 +102,7 @@ def fix_metadata() -> None:
78
102
  print(f"{folder} no keywords")
79
103
  os.chdir(orig_folder)
80
104
  continue
81
- keywords = getattr(mod, "keywords")
105
+ keywords = mod.keywords
82
106
  if set(keywords) != set(repo.get_topics()):
83
107
  print(f"in {folder}...")
84
108
  print(f"keywords is [{keywords}]")
@@ -273,14 +297,15 @@ def runs_show_not_success() -> None:
273
297
  description="Pull all projects from github",
274
298
  configs=[
275
299
  ConfigGithub,
300
+ ConfigAlgo,
276
301
  ],
277
302
  )
278
303
  def pull_all() -> None:
279
304
  orig_folder = os.getcwd()
305
+ base_dir = get_base_dir()
280
306
  for repo in yield_repos():
281
- owner = repo.owner.login
282
307
  project = repo.name
283
- folder = os.path.join(owner, repo.name)
308
+ folder = get_repo_folder(repo)
284
309
  if os.path.isdir(folder):
285
310
  if not os.path.isfile(os.path.join(folder, ".skip")):
286
311
  print(f"project [{project}] exists, pulling it...")
@@ -296,13 +321,15 @@ def pull_all() -> None:
296
321
  else:
297
322
  print(f"project [{project}] exists, skipping it because of .skip file...")
298
323
  else:
299
- print(f"project [{project}] does not exists, cloning it from [{repo.ssh_url}]...")
324
+ print(f"project [{project}] does not exists, cloning it from [{repo.ssh_url}] to [{folder}]...")
325
+ os.makedirs(base_dir, exist_ok=True)
300
326
  subprocess.check_call(
301
327
  [
302
328
  "git",
303
329
  "clone",
304
330
  repo.ssh_url,
305
- ]
331
+ ],
332
+ cwd=base_dir,
306
333
  )
307
334
 
308
335
 
@@ -315,10 +342,10 @@ def pull_all() -> None:
315
342
  )
316
343
  def clone_all() -> None:
317
344
  all_repo_folders = set()
345
+ base_dir = get_base_dir()
318
346
  for repo in yield_repos():
319
- owner = repo.owner.login
320
347
  project = repo.name
321
- folder = os.path.join(owner, repo.name)
348
+ folder = get_repo_folder(repo)
322
349
  all_repo_folders.add(folder)
323
350
  # print(f"considering [{project}] from [{repo.ssh_url}]...")
324
351
  if os.path.isfile(folder):
@@ -329,21 +356,20 @@ def clone_all() -> None:
329
356
  continue
330
357
  if ConfigAlgo.dryrun:
331
358
  continue
332
- print(f"cloning [{owner}:{project}] from [{repo.ssh_url}] to [{folder}]")
333
- if not os.path.isdir(owner):
334
- os.mkdir(owner)
359
+ print(f"cloning [{project}] from [{repo.ssh_url}] to [{folder}]")
360
+ os.makedirs(base_dir, exist_ok=True)
335
361
  subprocess.check_call(
336
362
  [
337
363
  "git",
338
364
  "clone",
339
365
  repo.ssh_url,
340
366
  ],
341
- cwd=owner,
367
+ cwd=base_dir,
342
368
  stdout=subprocess.DEVNULL,
343
369
  stderr=subprocess.DEVNULL,
344
370
  )
345
371
  if ConfigAlgo.show_extra:
346
- disk_repos = get_all_git_repos()
372
+ disk_repos = get_all_git_repos(base_dir)
347
373
  extra_repos = disk_repos - all_repo_folders
348
374
  if extra_repos:
349
375
  print("extra repos follow:")
@@ -368,6 +394,34 @@ def workflows_run() -> None:
368
394
  print(f"{ret}")
369
395
 
370
396
 
397
+ @register_endpoint(
398
+ description="Show all configuration options and their current values",
399
+ configs=[
400
+ ConfigGithub,
401
+ ConfigAlgo,
402
+ ConfigOutput,
403
+ ],
404
+ )
405
+ def config() -> None:
406
+ for config_class in [ConfigGithub, ConfigAlgo, ConfigOutput]:
407
+ print(f"{config_class.__name__}: {get_first_line(config_class, 'undocumented')}")
408
+ for name, param in the_registry.yield_name_data_for_config(config_class):
409
+ value = getattr(config_class, name)
410
+ if param.required and value is param.default:
411
+ value_str = "MANDATORY (not set)"
412
+ else:
413
+ value_str = param.t2s(value)
414
+ print(f" {name} [{param.get_type_name()}]: {value_str}")
415
+ print(f" {param.help_string}")
416
+ if not param.required:
417
+ print(f" default: {param.t2s(param.default)}")
418
+ more_help = param.more_help()
419
+ if more_help is not None:
420
+ print(f" {more_help}")
421
+ print()
422
+ print(f"repos are cloned to and pulled from: {get_base_dir()}/<repo-name>")
423
+
424
+
371
425
  @register_main(
372
426
  main_description=pygitpub.static.DESCRIPTION,
373
427
  app_name=pygitpub.static.APP_NAME,
@@ -1,5 +1,5 @@
1
1
  """ version which can be consumed from within the module """
2
- VERSION_STR = "0.0.58"
2
+ VERSION_STR = "0.0.59"
3
3
  DESCRIPTION = "Pygitpub will help you work with github"
4
4
  APP_NAME = "pygitpub"
5
5
  LOGGER_NAME = "pygitpub"
@@ -1,9 +1,9 @@
1
1
  """ misc.py """
2
2
 
3
+ import glob
3
4
  import logging
4
5
  import os
5
6
  import os.path
6
- import glob
7
7
 
8
8
  from pygitpub import LOGGER_NAME
9
9
 
@@ -28,8 +28,8 @@ def delete(workflow_run):
28
28
  return status == 204
29
29
 
30
30
 
31
- def get_all_git_repos() -> set[str]:
32
- """ we use */*/.git here with recursive=False (which is the default)
31
+ def get_all_git_repos(base_dir: str) -> set[str]:
32
+ """ we use */.git under base_dir with recursive=False (which is the default)
33
33
  because otherwise we would find submodules too
34
34
  """
35
- return {os.path.dirname(x) for x in glob.glob("*/*/.git")}
35
+ return {os.path.dirname(x) for x in glob.glob(os.path.join(base_dir, "*/.git"))}