cmem-cmemc 26.1.0rc3__tar.gz → 26.1.0rc4__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 (61) hide show
  1. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/PKG-INFO +2 -2
  2. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/cli.py +17 -2
  3. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/package.py +54 -1
  4. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/context.py +42 -2
  5. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/pyproject.toml +2 -2
  6. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/LICENSE +0 -0
  7. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/README-public.md +0 -0
  8. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/__init__.py +0 -0
  9. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/_cmemc.zsh +0 -0
  10. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/command.py +0 -0
  11. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/command_group.py +0 -0
  12. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/__init__.py +0 -0
  13. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/acl.py +0 -0
  14. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/admin.py +0 -0
  15. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/client.py +0 -0
  16. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/config.py +0 -0
  17. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/dataset.py +0 -0
  18. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/file.py +0 -0
  19. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/graph.py +0 -0
  20. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/graph_imports.py +0 -0
  21. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/graph_insights.py +0 -0
  22. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/manual.py +0 -0
  23. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/metrics.py +0 -0
  24. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/migration.py +0 -0
  25. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/project.py +0 -0
  26. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/python.py +0 -0
  27. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/query.py +0 -0
  28. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/scheduler.py +0 -0
  29. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/store.py +0 -0
  30. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/user.py +0 -0
  31. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/validation.py +0 -0
  32. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/variable.py +0 -0
  33. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/vocabulary.py +0 -0
  34. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/workflow.py +0 -0
  35. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/commands/workspace.py +0 -0
  36. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/completion.py +0 -0
  37. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/config_parser.py +0 -0
  38. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/constants.py +0 -0
  39. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/exceptions.py +0 -0
  40. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/manual_helper/__init__.py +0 -0
  41. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/manual_helper/graph.py +0 -0
  42. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/manual_helper/multi_page.py +0 -0
  43. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/manual_helper/single_page.py +0 -0
  44. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/__init__.py +0 -0
  45. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/abc.py +0 -0
  46. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/access_conditions_243.py +0 -0
  47. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/bootstrap_data.py +0 -0
  48. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/remove_noop_triple_251.py +0 -0
  49. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/shapes_widget_integrations_243.py +0 -0
  50. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/sparql_query_texts_242.py +0 -0
  51. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/migrations/workspace_configurations.py +0 -0
  52. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/object_list.py +0 -0
  53. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/parameter_types/__init__.py +0 -0
  54. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/parameter_types/path.py +0 -0
  55. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/placeholder.py +0 -0
  56. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/smart_path/__init__.py +0 -0
  57. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/smart_path/clients/__init__.py +0 -0
  58. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/smart_path/clients/http.py +0 -0
  59. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/string_processor.py +0 -0
  60. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/title_helper.py +0 -0
  61. {cmem_cmemc-26.1.0rc3 → cmem_cmemc-26.1.0rc4}/cmem_cmemc/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmem-cmemc
3
- Version: 26.1.0rc3
3
+ Version: 26.1.0rc4
4
4
  Summary: Command line client for eccenca Corporate Memory
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -32,7 +32,7 @@ Requires-Dist: certifi (>=2024.2.2)
32
32
  Requires-Dist: click (>=8.3.0,<9.0.0)
33
33
  Requires-Dist: click-didyoumean (>=0.3.1,<0.4.0)
34
34
  Requires-Dist: click-help-colors (>=0.9.4,<0.10.0)
35
- Requires-Dist: cmem-client (==0.7.1)
35
+ Requires-Dist: cmem-client (==0.8.0)
36
36
  Requires-Dist: cmem-cmempy (==25.4.0)
37
37
  Requires-Dist: configparser (>=7.2.0,<8.0.0)
38
38
  Requires-Dist: humanize (>=4.14.0,<5.0.0)
@@ -68,7 +68,21 @@ CONTEXT_SETTINGS = {"auto_envvar_prefix": "CMEMC", "help_option_names": ["-h", "
68
68
  )
69
69
  @click.option("-q", "--quiet", is_flag=True, help="Suppress any non-error info messages.")
70
70
  @click.option(
71
- "-d", "--debug", is_flag=True, help="Output debug messages and stack traces after errors."
71
+ "-d",
72
+ "--debug",
73
+ is_flag=True,
74
+ help="Output debug messages and stack traces after errors. The log level "
75
+ "can be set in the config via the CMEMC_LOG_LEVEL environment variable. "
76
+ "Options are: 'TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'"
77
+ " [default: DEBUG]",
78
+ )
79
+ @click.option(
80
+ "--log-level",
81
+ type=click.Choice(
82
+ ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False
83
+ ),
84
+ help="Set the log level when --debug is enabled. "
85
+ "Can also be set via CMEMC_LOG_LEVEL environment variable.",
72
86
  )
73
87
  @click.option(
74
88
  "--external-http-timeout",
@@ -89,6 +103,7 @@ def cli( # noqa: PLR0913
89
103
  config_file: str,
90
104
  connection: str,
91
105
  external_http_timeout: int,
106
+ log_level: str,
92
107
  ) -> None:
93
108
  """Eccenca Corporate Memory Control (cmemc).
94
109
 
@@ -111,7 +126,7 @@ def cli( # noqa: PLR0913
111
126
 
112
127
  cmemc is © 2026 eccenca GmbH, licensed under the Apache License 2.0.
113
128
  """
114
- _ = connection, debug, quiet, config_file, external_http_timeout
129
+ _ = connection, debug, quiet, config_file, external_http_timeout, log_level
115
130
  if " ".join(sys.argv).find("config edit") != -1:
116
131
  app = ApplicationContext(config_file=config_file, debug=debug, quiet=quiet)
117
132
  else:
@@ -103,7 +103,7 @@ def _complete_marketplace_package_ids(
103
103
  ApplicationContext.set_connection_from_params(ctx.find_root().params)
104
104
  client = Client.from_cmempy()
105
105
  marketplace = Marketplace(client=client)
106
- candidates = [(_.id, f"{_.name} ({_.type})") for _ in marketplace.get_available_packages()]
106
+ candidates = [(_.id, f"{_.name}") for _ in marketplace.get_available_packages()]
107
107
  return completion.finalize_completion(candidates=candidates, incomplete=incomplete)
108
108
 
109
109
 
@@ -499,6 +499,58 @@ def publish_command(app: ApplicationContext, package_archive: str, marketplace_u
499
499
  app.echo_success("done")
500
500
 
501
501
 
502
+ @click.command(cls=CmemcCommand, name="search")
503
+ @click.argument(
504
+ "SEARCH_TERMS",
505
+ nargs=-1,
506
+ type=click.STRING,
507
+ )
508
+ @click.option("--raw", is_flag=True, help="Outputs raw JSON.")
509
+ @click.pass_obj
510
+ def search_command(app: ApplicationContext, search_terms: tuple[str], raw: bool) -> None:
511
+ """Search for available packages with a given search text."""
512
+ packages = app.client.marketplace.get_available_packages()
513
+
514
+ available_packages = []
515
+ search_terms_lower = [term.lower() for term in search_terms]
516
+
517
+ if search_terms == "":
518
+ available_packages = packages
519
+ else:
520
+ for package in packages:
521
+ searchable_text = (
522
+ f"{package.id.lower()} {package.name.lower()} "
523
+ f"{package.description.lower()} {package.type.value.lower()}"
524
+ )
525
+ if all(term in searchable_text for term in search_terms_lower):
526
+ available_packages.append(package)
527
+
528
+ if raw:
529
+ package_data = [
530
+ json.loads(metadata.model_dump_json(indent=2)) for metadata in available_packages
531
+ ]
532
+ app.echo_info_json(package_data)
533
+ return
534
+
535
+ table = [
536
+ (
537
+ _.id,
538
+ _.name,
539
+ _.description,
540
+ _.type.value,
541
+ )
542
+ for _ in available_packages
543
+ ]
544
+ app.echo_info_table(
545
+ table,
546
+ headers=["ID", "Name", "Description", "Type"],
547
+ sort_column=0,
548
+ empty_table_message="No available packages found.",
549
+ caption=f"{len(available_packages)} package{'' if len(available_packages) == 1 else "s" } "
550
+ f"found on marketplace.eccenca.dev",
551
+ )
552
+
553
+
502
554
  @click.group(cls=CmemcGroup)
503
555
  def package_group() -> CmemcGroup: # type: ignore[empty-body]
504
556
  """List, (un)install, export, create, or inspect packages."""
@@ -511,3 +563,4 @@ package_group.add_command(uninstall_command)
511
563
  package_group.add_command(export_command)
512
564
  package_group.add_command(build_command)
513
565
  package_group.add_command(publish_command)
566
+ package_group.add_command(search_command)
@@ -3,6 +3,7 @@
3
3
  import ast
4
4
  import configparser
5
5
  import json
6
+ import logging
6
7
  import os
7
8
  import re
8
9
  import subprocess # nosec
@@ -57,6 +58,19 @@ KNOWN_SECRET_KEYS = ("OAUTH_PASSWORD", "OAUTH_CLIENT_SECRET", "OAUTH_ACCESS_TOKE
57
58
  SSL_VERIFY_WARNING = "SSL verification is disabled (SSL_VERIFY=False)."
58
59
 
59
60
 
61
+ class ClickHandler(logging.Handler):
62
+ """Custom logging handler that outputs using click.secho with dimming."""
63
+
64
+ def emit(self, record: logging.LogRecord) -> None:
65
+ """Emit a log record using click.secho."""
66
+ try:
67
+ now = datetime.now(tz=timezone.utc)
68
+ msg = self.format(record)
69
+ click.secho(f"[{now!s}] {msg}", err=True, dim=True)
70
+ except Exception: # noqa: BLE001
71
+ self.handleError(record)
72
+
73
+
60
74
  def build_caption(
61
75
  count: int,
62
76
  item_name: str,
@@ -101,12 +115,14 @@ class ApplicationContext:
101
115
  connection: str | None
102
116
  console: Console
103
117
  console_width: int | None = None
118
+ log_level: str | None = None
104
119
 
105
120
  def __init__(
106
121
  self,
107
122
  config_file: str,
108
123
  connection: str | None = None,
109
124
  debug: bool = False,
125
+ log_level: str | None = None,
110
126
  quiet: bool = False,
111
127
  ):
112
128
  """Initialize main context."""
@@ -114,6 +130,7 @@ class ApplicationContext:
114
130
  self.app_name = "cmemc"
115
131
  self.set_debug(debug)
116
132
  self.set_quiet(quiet)
133
+ self.log_level = log_level
117
134
  self.ensure_app_config_dir()
118
135
  self.set_connection(connection)
119
136
  self.set_external_http_timeout(self.DEFAULT_EXTERNAL_HTTP_TIMEOUT)
@@ -123,7 +140,18 @@ class ApplicationContext:
123
140
  @property
124
141
  def client(self) -> Client:
125
142
  """The cmem_client Client object."""
126
- return Client.from_cmempy()
143
+ client = Client.from_cmempy()
144
+ if self.debug:
145
+ level = self.log_level or "DEBUG"
146
+ handler = ClickHandler()
147
+ formatter = logging.Formatter("%(name)s - [%(levelname)s] - %(message)s")
148
+ handler.setFormatter(formatter)
149
+ client.configure_client_logger(
150
+ level=level,
151
+ format_string="%(name)s - [%(levelname)s] - %(message)s",
152
+ handlers=[handler],
153
+ )
154
+ return client
127
155
 
128
156
  @staticmethod
129
157
  def from_params(params: dict) -> "ApplicationContext":
@@ -134,6 +162,7 @@ class ApplicationContext:
134
162
  config_file = params.get("config_file")
135
163
  debug = str_to_bool(str(params.get("debug")))
136
164
  quiet = str_to_bool(str(params.get("quiet")))
165
+ log_level = str(params.get("log_level")) if params.get("log_level") else None
137
166
  external_http_timeout = (
138
167
  int(str(params.get("external_http_timeout")))
139
168
  if "external_http_timeout" in params
@@ -143,7 +172,11 @@ class ApplicationContext:
143
172
  if not config_file:
144
173
  raise ValueError("Missing required key: 'config_file' in config dictionary")
145
174
  app = ApplicationContext(
146
- config_file=config_file, connection=connection, debug=debug, quiet=quiet
175
+ config_file=config_file,
176
+ connection=connection,
177
+ debug=debug,
178
+ quiet=quiet,
179
+ log_level=log_level,
147
180
  )
148
181
  app.set_external_http_timeout(external_http_timeout)
149
182
  if not app.debug:
@@ -184,6 +217,10 @@ class ApplicationContext:
184
217
  """Set debug state"""
185
218
  self.debug = debug
186
219
 
220
+ def set_log_level(self, log_level: str) -> None:
221
+ """Set log level"""
222
+ self.log_level = log_level
223
+
187
224
  def set_quiet(self, quiet: bool = False) -> None:
188
225
  """Set quiets state"""
189
226
  self.quiet = quiet
@@ -247,6 +284,9 @@ class ApplicationContext:
247
284
  for key, value in config.items():
248
285
  if key == "CMEMC_DEBUG":
249
286
  self.set_debug(str_to_bool(value))
287
+ elif key == "CMEMC_LOG_LEVEL":
288
+ self.set_log_level(value)
289
+ self.echo_debug(f"set log level to {value}")
250
290
  elif key == "CMEMC_QUIET":
251
291
  self.set_quiet(str_to_bool(value))
252
292
  elif key == "CMEMC_CONNECTION":
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cmem-cmemc"
3
- version = "26.1.0rc3"
3
+ version = "26.1.0rc4"
4
4
  description = "Command line client for eccenca Corporate Memory"
5
5
  license = "Apache-2.0"
6
6
  classifiers = [
@@ -36,7 +36,7 @@ click-didyoumean = "^0.3.1"
36
36
  click-help-colors = "^0.9.4"
37
37
  cmem-cmempy = "==25.4.0"
38
38
  # cmem-cmempy = { path = "cmempy", develop = true }
39
- cmem-client = "=0.7.1"
39
+ cmem-client = "=0.8.0"
40
40
  # cmem-client = { path = "cmem-client", develop = true }
41
41
  configparser = "^7.2.0"
42
42
  jinja2 = "^3.1.6"
File without changes