poetry-plugin-ivcap 0.2.0__py3-none-any.whl → 0.2.3__py3-none-any.whl

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.
@@ -3,15 +3,16 @@
3
3
  # Use of this source code is governed by a BSD-style license that can be
4
4
  # found in the LICENSE file. See the AUTHORS file for names of contributors.
5
5
  #
6
+ from datetime import datetime, timezone
6
7
  import os
7
8
  import re
8
9
  import sys
9
10
  import tempfile
10
- from typing import Dict, Optional
11
+ from typing import Dict, List, Optional
11
12
  from pydantic import BaseModel, Field
12
13
  import subprocess
13
14
 
14
- from .util import command_exists, get_name
15
+ from .util import command_exists, get_name, get_version
15
16
 
16
17
  DOCKER_BUILD_TEMPLATE = """
17
18
  docker buildx build
@@ -23,12 +24,20 @@ docker buildx build
23
24
  --load #PROJECT_DIR#
24
25
  """
25
26
 
26
- DOCKER_RUN_TEMPLATE = """
27
+ DOCKER_LAMBDA_RUN_TEMPLATE = """
27
28
  docker run -it
28
29
  -p #PORT#:#PORT#
29
30
  --platform=linux/#ARCH#
30
31
  --rm \
31
- #NAME#_#ARCH#:#TAG# --port #PORT#
32
+ #NAME#_#ARCH#:#TAG#
33
+ """
34
+
35
+ DOCKER_BATCH_RUN_TEMPLATE = """
36
+ docker run -it
37
+ --platform=linux/#ARCH#
38
+ -v #PROJECT_DIR#:/data
39
+ --rm \
40
+ #NAME#_#ARCH#:#TAG#
32
41
  """
33
42
 
34
43
  class DockerConfig(BaseModel):
@@ -37,7 +46,7 @@ class DockerConfig(BaseModel):
37
46
  arch: Optional[str] = Field(None)
38
47
  version: Optional[str] = Field(None)
39
48
  dockerfile: Optional[str] = Field("Dockerfile")
40
- project_dir: Optional[str] = Field(".")
49
+ project_dir: Optional[str] = Field(os.getcwd())
41
50
 
42
51
  @property
43
52
  def docker_name(self) -> str:
@@ -57,26 +66,61 @@ class DockerConfig(BaseModel):
57
66
  .split()
58
67
  return t
59
68
 
60
- def from_run_template(self, data) -> str:
69
+ def from_run_template(self, data, args, line) -> List[any]:
61
70
  pdata = data.get("tool", {}).get("poetry-plugin-ivcap", {})
62
- template = pdata.get("docker-run-template", DOCKER_RUN_TEMPLATE).strip()
63
- opts = pdata.get("docker-run-opts", {"port": 8080})
71
+ template = pdata.get("docker-run-template")
72
+ if template is None:
73
+ smode = pdata.get("service-type")
74
+ if smode is None:
75
+ line("<error>ERROR: 'service-type' is not defined in [poetry-plugin-ivcap]</error>")
76
+ sys.exit(1)
77
+ if smode == "lambda":
78
+ template = DOCKER_LAMBDA_RUN_TEMPLATE
79
+ elif smode == "batch":
80
+ template = DOCKER_BATCH_RUN_TEMPLATE
81
+ else:
82
+ line(f"<error>ERROR: Unknown service type '{smode}' in [poetry-plugin-ivcap]</error>")
83
+ sys.exit(1)
84
+
85
+ opts = pdata.get("docker-run-opts", [])
86
+ port = get_port_value(args)
87
+ port_in_args = True
88
+ if not port:
89
+ port = get_port_value(opts)
90
+ if not port:
91
+ port_in_args = False
92
+ port = str(pdata.get("port", 8000))
93
+
94
+ # elif opts.get('port') is not None:
95
+ # opts['port'] = opts.get('port')
96
+ # elif '--port' in template:
97
+ # # If the template has a port but no port is provided, use a default
98
+ # opts['port'] = '8000'
99
+
64
100
  t = template.strip()\
65
101
  .replace("#NAME#", self.name)\
66
102
  .replace("#TAG#", self.tag)\
103
+ .replace("#PORT#", port)\
67
104
  .replace("#ARCH#", self.arch)\
68
105
  .replace("#VERSION#", self.version)\
69
106
  .replace("#PROJECT_DIR#", self.project_dir)
70
107
 
71
- for key, value in opts.items():
72
- t = t.replace(f"#{key.upper()}#", str(value))
108
+ # for key, value in opts.items():
109
+ # if key != "port":
110
+ # t = t.replace(f"#{key.upper()}#", str(value))
73
111
 
74
- return t.split()
112
+
113
+ cmd = t.split()
114
+ cmd.extend(opts)
115
+ cmd.extend(args)
116
+ if smode == "lambda" and not port_in_args:
117
+ cmd.extend(["--port", port])
118
+ return cmd
75
119
 
76
120
 
77
121
  def docker_build(data: dict, line, arch = None) -> None:
78
122
  check_docker_cmd(line)
79
- config = _docker_cfg(data, line, arch)
123
+ config = docker_cfg(data, line, arch)
80
124
  build_cmd = config.from_build_template(data)
81
125
  line(f"<info>INFO: {' '.join(build_cmd)}</info>")
82
126
  process = subprocess.Popen(build_cmd, stdout=sys.stdout, stderr=sys.stderr)
@@ -87,10 +131,10 @@ def docker_build(data: dict, line, arch = None) -> None:
87
131
  line("<info>INFO: Docker build completed successfully</info>")
88
132
  return config.docker_name
89
133
 
90
- def docker_run(data: dict, line) -> None:
134
+ def docker_run(data: dict, args, line) -> None:
91
135
  check_docker_cmd(line)
92
- config = _docker_cfg(data, line)
93
- build_run = config.from_run_template(data)
136
+ config = docker_cfg(data, line)
137
+ build_run = config.from_run_template(data, args, line)
94
138
  line(f"<info>INFO: {' '.join(build_run)}</info>")
95
139
  process = subprocess.Popen(build_run, stdout=sys.stdout, stderr=sys.stderr)
96
140
  exit_code = process.wait()
@@ -99,24 +143,15 @@ def docker_run(data: dict, line) -> None:
99
143
  else:
100
144
  line("<info>INFO: Docker run completed successfully</info>")
101
145
 
102
- def _docker_cfg(data: dict, line, arch = None) -> DockerConfig:
103
- project_data = data.get("project", {})
146
+ def docker_cfg(data: dict, line, arch = None) -> DockerConfig:
104
147
  name = get_name(data)
105
- version = project_data.get("version", None)
106
148
 
107
149
  pdata = data.get("tool", {}).get("poetry-plugin-ivcap", {})
108
- config = DockerConfig(name=name, version=version, **pdata.get("docker", {}))
150
+ config = DockerConfig(name=name, **pdata.get("docker", {}))
109
151
  if arch:
110
152
  # override architecture if provided
111
153
  config.arch = arch
112
154
 
113
- if not config.version:
114
- try:
115
- config.version = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode().strip()
116
- except Exception as e:
117
- line(f"<error>Error retrieving latest tag: {e}</error>")
118
- config.version = "???"
119
-
120
155
  if not config.tag:
121
156
  try:
122
157
  config.tag = subprocess.check_output(['git', 'rev-parse', "--short", 'HEAD']).decode().strip()
@@ -124,6 +159,9 @@ def _docker_cfg(data: dict, line, arch = None) -> DockerConfig:
124
159
  line(f"<warning>WARN: retrieving commit hash: {e}</warning>")
125
160
  config.tag = "latest"
126
161
 
162
+ if not config.version:
163
+ config.version = get_version(data, config.tag, line)
164
+
127
165
  if not config.arch:
128
166
  try:
129
167
  config.arch = subprocess.check_output(['uname', '-m']).decode().strip()
@@ -183,3 +221,12 @@ def check_docker_cmd(line):
183
221
  if not command_exists("docker"):
184
222
  line("<error>'docker' command not found. Please install it first.</error>")
185
223
  sys.exit(1)
224
+
225
+ def get_port_value(arr):
226
+ try:
227
+ index = arr.index('--port')
228
+ return arr[index + 1]
229
+ except ValueError:
230
+ return None
231
+ except IndexError:
232
+ return None
@@ -11,14 +11,12 @@ import tempfile
11
11
  import uuid
12
12
  import humanize
13
13
 
14
- from .docker import _docker_cfg, docker_build, docker_push
14
+ from .docker import docker_cfg, docker_build, docker_push
15
15
  from .util import command_exists, execute_subprocess_and_capture_output, get_name, string_to_number
16
16
 
17
17
  def docker_publish(data, line):
18
18
  check_ivcap_cmd(line)
19
- #dname = docker_build(data, line, arch="amd64")
20
- dname = "gene_onology_term_mapper_amd64:9a9a7cc"
21
- print(dname)
19
+ dname = docker_build(data, line, arch="amd64")
22
20
 
23
21
  size_cmd = ["docker", "inspect", "--format='{{.Size}}'", dname]
24
22
  line(f"<debug>Running: {' '.join(size_cmd)} </debug>")
@@ -38,7 +36,7 @@ def service_register(data, line):
38
36
  line("<error>Missing 'service-file' in [tool.poetry-plugin-ivcap]</error>")
39
37
  return
40
38
 
41
- dcfg = _docker_cfg(data, line, "amd64")
39
+ dcfg = docker_cfg(data, line, "amd64")
42
40
  pkg_cmd = ["ivcap", "package", "list", dcfg.docker_name]
43
41
  line(f"<debug>Running: {' '.join(pkg_cmd)} </debug>")
44
42
  pkg = subprocess.check_output(pkg_cmd).decode()
@@ -3,11 +3,14 @@
3
3
  # Use of this source code is governed by a BSD-style license that can be
4
4
  # found in the LICENSE file. See the AUTHORS file for names of contributors.
5
5
  #
6
+ import os
6
7
  from poetry.plugins.application_plugin import ApplicationPlugin
7
8
  from cleo.commands.command import Command
8
9
  from cleo.helpers import argument
9
10
  import subprocess
10
11
 
12
+ from poetry_plugin_ivcap.util import get_version
13
+
11
14
  from .ivcap import create_service_id, service_register, tool_register
12
15
  from .docker import docker_build, docker_run
13
16
  from .ivcap import docker_publish
@@ -31,9 +34,9 @@ Available subcommands:
31
34
  tool-register Register the service as an AI Tool with IVCAP
32
35
 
33
36
  Example:
34
- poetry ivcap run
37
+ poetry ivcap run -- --port 8080
35
38
 
36
- Configurable optiosn in pyproject.toml:
39
+ Configurable options in pyproject.toml:
37
40
 
38
41
  [tool.poetry-plugin-ivcap]
39
42
  service-file = "service.py" # The Python file that implements the service
@@ -44,20 +47,26 @@ Configurable optiosn in pyproject.toml:
44
47
  docker-run-template = "docker run -rm -p #PORT#:#PORT#"
45
48
  """
46
49
  arguments = [
47
- argument("subcommand", optional=True, description="Subcommand: run, deploy, etc.")
50
+ argument("subcommand", optional=True, description="Subcommand: run, deploy, etc."),
51
+ argument("args", optional=True, multiple=True, description="Additional arguments for subcommand"),
48
52
  ]
49
53
 
54
+ allow_extra_args = True
55
+ ignore_validation_errors = True
56
+
50
57
  def handle(self):
51
58
  poetry = self.application.poetry
52
59
  data = poetry.pyproject.data
53
60
 
54
61
  sub = self.argument("subcommand")
62
+ args = self.argument("args")
63
+
55
64
  if sub == "run":
56
- self.run_service(data)
65
+ self.run_service(data, args, self.line)
57
66
  elif sub == "docker-build":
58
67
  docker_build(data, self.line)
59
68
  elif sub == "docker-run":
60
- docker_run(data, self.line)
69
+ docker_run(data, args, self.line)
61
70
  elif sub == "docker-publish":
62
71
  docker_publish(data, self.line)
63
72
  elif sub == "service-register":
@@ -72,18 +81,26 @@ Configurable optiosn in pyproject.toml:
72
81
  self.line(f"<error>Unknown subcommand: {sub}</error>")
73
82
  print(self.help)
74
83
 
75
- def run_service(self, data):
84
+ def run_service(self, data, args, line):
76
85
  config = data.get("tool", {}).get("poetry-plugin-ivcap", {})
77
86
 
78
87
  service = config.get("service-file")
79
- port = config.get("port")
80
-
81
- if not service or not port:
82
- self.line("<error>Missing 'service-file' or 'port' in [tool.poetry-plugin-ivcap]</error>")
88
+ if not service:
89
+ self.line("<error>Missing 'service-file' in [tool.poetry-plugin-ivcap]</error>")
83
90
  return
84
91
 
85
- self.line(f"<info>Running: python {service} --port {port}</info>")
86
- subprocess.run(["poetry", "run", "python", service, "--port", str(port)])
92
+ if not '--port' in args:
93
+ port = config.get("port")
94
+ if port:
95
+ args.extend(["--port", str(port)])
96
+
97
+ env = os.environ.copy()
98
+ env["VERSION"] = get_version(data, None, line)
99
+
100
+ cmd = ["poetry", "run", "python", service]
101
+ cmd.extend(args)
102
+ self.line(f"<info>Running: {' '.join(cmd)}</info>")
103
+ subprocess.run(cmd, env=env)
87
104
 
88
105
  class IvcapPlugin(ApplicationPlugin):
89
106
  def activate(self, application):
@@ -5,6 +5,7 @@
5
5
  #
6
6
  import subprocess
7
7
  import platform
8
+ from datetime import datetime, timezone
8
9
 
9
10
  def get_name(data) -> str:
10
11
  project_data = data.get("project", {})
@@ -85,3 +86,23 @@ def execute_subprocess_and_capture_output(command):
85
86
  full_output += line
86
87
 
87
88
  return full_output
89
+
90
+ def get_version(data, tag, line):
91
+ if not tag:
92
+ try:
93
+ tag = subprocess.check_output(['git', 'rev-parse', "--short", 'HEAD']).decode().strip()
94
+ except Exception as e:
95
+ line(f"<warning>WARN: retrieving commit hash: {e}</warning>")
96
+ tag = "latest"
97
+
98
+ project_data = data.get("project", {})
99
+ v = project_data.get("version", None)
100
+ if not v:
101
+ try:
102
+ v = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0']).decode().strip()
103
+ except Exception as e:
104
+ line(f"<error>Error retrieving latest tag: {e}</error>")
105
+ v = "???"
106
+ now = datetime.now(timezone.utc).astimezone()
107
+ version = f"{v}|{tag}|{now.replace(microsecond=0).isoformat()}"
108
+ return version
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: poetry-plugin-ivcap
3
- Version: 0.2.0
3
+ Version: 0.2.3
4
4
  Summary: A custom Poetry command for IVCAP deployments
5
5
  License: MIT
6
6
  Author: Max Ott
7
7
  Author-email: max.ott@csiro.au
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.9
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.9
@@ -27,9 +27,11 @@ Add to your `pyproject.toml`:
27
27
 
28
28
  ```toml
29
29
  [tool.poetry-plugin-ivcap]
30
- default_target = "docker"
31
- docker_tag = "myapp:dev"
32
- ```
30
+ service-file = "service.py"
31
+ service-id = "urn:ivcap:service:ac158a1f-dfb4-5dac-bf2e-9bf15e0f2cc7"
32
+ port = 8077
33
+ # docker
34
+ # docker-run-opts = { port = 8079 }```
33
35
 
34
36
  ## Installation
35
37
 
@@ -0,0 +1,10 @@
1
+ poetry_plugin_ivcap/docker.py,sha256=9frC-F5S3p3vP-4mrLSlqvtx9ayNZ-xZXsMkWAtg4y4,7952
2
+ poetry_plugin_ivcap/ivcap.py,sha256=5s9y6U-nhdmTh-Xeek8l_oCJvnufYa485GAlr4GE_Xs,5624
3
+ poetry_plugin_ivcap/plugin.py,sha256=v_lW-G4FE1l2SmKt_Xfx-mrwxabWhWjsKsbvGS0h7HY,3869
4
+ poetry_plugin_ivcap/util.py,sha256=bcjjbKoV_pAgeuC7Ws9XbJa3phFpNVyrRAlFJ1VubRg,3429
5
+ poetry_plugin_ivcap-0.2.3.dist-info/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
6
+ poetry_plugin_ivcap-0.2.3.dist-info/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
7
+ poetry_plugin_ivcap-0.2.3.dist-info/METADATA,sha256=nwCpQpWqEhvm6ATx8lQ4R_tlKZZiUPqQKUONo0P7_hw,3029
8
+ poetry_plugin_ivcap-0.2.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
9
+ poetry_plugin_ivcap-0.2.3.dist-info/entry_points.txt,sha256=3xagEFBkGgrVe8WyjmhlHLr4JDEWPN_W4DwxnIBWbNY,74
10
+ poetry_plugin_ivcap-0.2.3.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- poetry_plugin_ivcap/docker.py,sha256=ePxs6RdDm0znmGpnybd1O-N9Ef1WjUX5eqRV0xf7pKk,6609
2
- poetry_plugin_ivcap/ivcap.py,sha256=wXy1IGiOiNA8r9tJIE0_hhamQn6J6pXzc0Zc33euO3k,5697
3
- poetry_plugin_ivcap/plugin.py,sha256=hF-eTV6ZWuqIXcCLINxvQeCvRzZCXuaIVaHoSztmqIg,3357
4
- poetry_plugin_ivcap/util.py,sha256=cAfofL50KZAcaghkDuizGMoW8qx3hkH3A0h9S9DraZc,2620
5
- poetry_plugin_ivcap-0.2.0.dist-info/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
6
- poetry_plugin_ivcap-0.2.0.dist-info/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
7
- poetry_plugin_ivcap-0.2.0.dist-info/METADATA,sha256=iVRyAjK4St7FUKR0jgnYLf-97XI3NOaQCGjCgd2uxho,2931
8
- poetry_plugin_ivcap-0.2.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
9
- poetry_plugin_ivcap-0.2.0.dist-info/entry_points.txt,sha256=3xagEFBkGgrVe8WyjmhlHLr4JDEWPN_W4DwxnIBWbNY,74
10
- poetry_plugin_ivcap-0.2.0.dist-info/RECORD,,