fractal-server 2.12.0a0__py3-none-any.whl → 2.12.1__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.
fractal_server/config.py CHANGED
@@ -11,7 +11,6 @@
11
11
  # <exact-lab.it> under contract with Liberali Lab from the Friedrich Miescher
12
12
  # Institute for Biomedical Research and Pelkmans Lab from the University of
13
13
  # Zurich.
14
- import json
15
14
  import logging
16
15
  import shutil
17
16
  import sys
@@ -46,16 +45,19 @@ class MailSettings(BaseModel):
46
45
  port: SMTP server port
47
46
  password: Sender password
48
47
  instance_name: Name of SMTP server instance
49
- use_starttls: Using or not security protocol
48
+ use_starttls: Whether to use the security protocol
49
+ use_login: Whether to use login
50
50
  """
51
51
 
52
52
  sender: EmailStr
53
53
  recipients: list[EmailStr] = Field(min_items=1)
54
54
  smtp_server: str
55
55
  port: int
56
- password: str
56
+ encrypted_password: Optional[str] = None
57
+ encryption_key: Optional[str] = None
57
58
  instance_name: str
58
59
  use_starttls: bool
60
+ use_login: bool
59
61
 
60
62
 
61
63
  class FractalConfigurationError(RuntimeError):
@@ -406,7 +408,7 @@ class Settings(BaseSettings):
406
408
  """
407
409
 
408
410
  @root_validator(pre=True)
409
- def check_tasks_python(cls, values) -> None:
411
+ def check_tasks_python(cls, values):
410
412
  """
411
413
  Perform multiple checks of the Python-interpreter variables.
412
414
 
@@ -416,7 +418,6 @@ class Settings(BaseSettings):
416
418
  `sys.executable` and set the corresponding
417
419
  `FRACTAL_TASKS_PYTHON_X_Y` (and unset all others).
418
420
  """
419
-
420
421
  # `FRACTAL_TASKS_PYTHON_X_Y` variables can only be absolute paths
421
422
  for version in ["3_9", "3_10", "3_11", "3_12"]:
422
423
  key = f"FRACTAL_TASKS_PYTHON_{version}"
@@ -575,66 +576,107 @@ class Settings(BaseSettings):
575
576
  ###########################################################################
576
577
  # SMTP SERVICE
577
578
  ###########################################################################
578
- FRACTAL_EMAIL_SETTINGS: Optional[str] = None
579
+
580
+ FRACTAL_EMAIL_SENDER: Optional[EmailStr] = None
581
+ """
582
+ Address of the OAuth-signup email sender.
579
583
  """
580
- Encrypted version of settings dictionary, with keys `sender`, `password`,
581
- `smtp_server`, `port`, `instance_name`, `use_starttls`.
584
+ FRACTAL_EMAIL_PASSWORD: Optional[str] = None
582
585
  """
583
- FRACTAL_EMAIL_SETTINGS_KEY: Optional[str] = None
586
+ Password for the OAuth-signup email sender.
587
+ """
588
+ FRACTAL_EMAIL_PASSWORD_KEY: Optional[str] = None
584
589
  """
585
590
  Key value for `cryptography.fernet` decrypt
586
591
  """
592
+ FRACTAL_EMAIL_SMTP_SERVER: Optional[str] = None
593
+ """
594
+ SMPT server for the OAuth-signup emails.
595
+ """
596
+ FRACTAL_EMAIL_SMTP_PORT: Optional[int] = None
597
+ """
598
+ SMPT server port for the OAuth-signup emails.
599
+ """
600
+ FRACTAL_EMAIL_INSTANCE_NAME: Optional[str] = None
601
+ """
602
+ Fractal instance name, to be included in the OAuth-signup emails.
603
+ """
587
604
  FRACTAL_EMAIL_RECIPIENTS: Optional[str] = None
588
605
  """
589
- List of email receivers, separated with commas
606
+ Comma-separated list of recipients of the OAuth-signup emails.
607
+ """
608
+ FRACTAL_EMAIL_USE_STARTTLS: Optional[bool] = True
609
+ """
610
+ Whether to use StartTLS when using the SMTP server.
611
+ """
612
+ FRACTAL_EMAIL_USE_LOGIN: Optional[bool] = True
590
613
  """
614
+ Whether to use login when using the SMTP server.
615
+ """
616
+ email_settings: Optional[MailSettings] = None
591
617
 
592
- @property
593
- def MAIL_SETTINGS(self) -> Optional[MailSettings]:
594
- if (
595
- self.FRACTAL_EMAIL_SETTINGS is not None
596
- and self.FRACTAL_EMAIL_SETTINGS_KEY is not None
597
- and self.FRACTAL_EMAIL_RECIPIENTS is not None
598
- ):
599
- smpt_settings = (
600
- Fernet(self.FRACTAL_EMAIL_SETTINGS_KEY)
601
- .decrypt(self.FRACTAL_EMAIL_SETTINGS)
602
- .decode("utf-8")
603
- )
604
- recipients = self.FRACTAL_EMAIL_RECIPIENTS.split(",")
605
- mail_settings = MailSettings(
606
- **json.loads(smpt_settings), recipients=recipients
607
- )
608
- return mail_settings
609
- elif not all(
610
- [
611
- self.FRACTAL_EMAIL_RECIPIENTS is None,
612
- self.FRACTAL_EMAIL_SETTINGS_KEY is None,
613
- self.FRACTAL_EMAIL_SETTINGS is None,
614
- ]
615
- ):
616
- raise ValueError(
617
- "You must set all SMPT config variables: "
618
- f"{self.FRACTAL_EMAIL_SETTINGS=}, "
619
- f"{self.FRACTAL_EMAIL_RECIPIENTS=}, "
620
- f"{self.FRACTAL_EMAIL_SETTINGS_KEY=}, "
618
+ @root_validator(pre=True)
619
+ def validate_email_settings(cls, values):
620
+ email_values = {
621
+ k: v for k, v in values.items() if k.startswith("FRACTAL_EMAIL")
622
+ }
623
+ if email_values:
624
+
625
+ def assert_key(key: str):
626
+ if key not in email_values:
627
+ raise ValueError(f"Missing '{key}'")
628
+
629
+ assert_key("FRACTAL_EMAIL_SENDER")
630
+ assert_key("FRACTAL_EMAIL_SMTP_SERVER")
631
+ assert_key("FRACTAL_EMAIL_SMTP_PORT")
632
+ assert_key("FRACTAL_EMAIL_INSTANCE_NAME")
633
+ assert_key("FRACTAL_EMAIL_RECIPIENTS")
634
+
635
+ if email_values.get("FRACTAL_EMAIL_USE_LOGIN", True):
636
+ if "FRACTAL_EMAIL_PASSWORD" not in email_values:
637
+ raise ValueError(
638
+ "'FRACTAL_EMAIL_USE_LOGIN' is True but "
639
+ "'FRACTAL_EMAIL_PASSWORD' is not provided."
640
+ )
641
+ elif "FRACTAL_EMAIL_PASSWORD_KEY" not in email_values:
642
+ raise ValueError(
643
+ "'FRACTAL_EMAIL_USE_LOGIN' is True but "
644
+ "'FRACTAL_EMAIL_PASSWORD_KEY' is not provided."
645
+ )
646
+ else:
647
+ try:
648
+ (
649
+ Fernet(email_values["FRACTAL_EMAIL_PASSWORD_KEY"])
650
+ .decrypt(email_values["FRACTAL_EMAIL_PASSWORD"])
651
+ .decode("utf-8")
652
+ )
653
+ except Exception as e:
654
+ raise ValueError(
655
+ "Invalid pair (FRACTAL_EMAIL_PASSWORD, "
656
+ "FRACTAL_EMAIL_PASSWORD_KEY). "
657
+ f"Original error: {str(e)}."
658
+ )
659
+
660
+ values["email_settings"] = MailSettings(
661
+ sender=email_values["FRACTAL_EMAIL_SENDER"],
662
+ recipients=email_values["FRACTAL_EMAIL_RECIPIENTS"].split(","),
663
+ smtp_server=email_values["FRACTAL_EMAIL_SMTP_SERVER"],
664
+ port=email_values["FRACTAL_EMAIL_SMTP_PORT"],
665
+ encrypted_password=email_values.get("FRACTAL_EMAIL_PASSWORD"),
666
+ encryption_key=email_values.get("FRACTAL_EMAIL_PASSWORD_KEY"),
667
+ instance_name=email_values["FRACTAL_EMAIL_INSTANCE_NAME"],
668
+ use_starttls=email_values.get(
669
+ "FRACTAL_EMAIL_USE_STARTTLS", True
670
+ ),
671
+ use_login=email_values.get("FRACTAL_EMAIL_USE_LOGIN", True),
621
672
  )
622
673
 
674
+ return values
675
+
623
676
  ###########################################################################
624
677
  # BUSINESS LOGIC
625
678
  ###########################################################################
626
679
 
627
- def check_fractal_mail_settings(self):
628
- """
629
- Checks that the mail settings are properly set.
630
- """
631
- try:
632
- self.MAIL_SETTINGS
633
- except Exception as e:
634
- raise FractalConfigurationError(
635
- f"Invalid email configuration settings. Original error: {e}"
636
- )
637
-
638
680
  def check_db(self) -> None:
639
681
  """
640
682
  Checks that db environment variables are properly set.
@@ -740,7 +782,6 @@ class Settings(BaseSettings):
740
782
 
741
783
  self.check_db()
742
784
  self.check_runner()
743
- self.check_fractal_mail_settings()
744
785
 
745
786
  def get_sanitized(self) -> dict:
746
787
  def _must_be_sanitized(string) -> bool:
fractal_server/main.py CHANGED
@@ -28,6 +28,7 @@ from .logger import get_logger
28
28
  from .logger import reset_logger_handlers
29
29
  from .logger import set_logger
30
30
  from .syringe import Inject
31
+ from fractal_server import __VERSION__
31
32
 
32
33
 
33
34
  def collect_routers(app: FastAPI) -> None:
@@ -67,7 +68,7 @@ def check_settings() -> None:
67
68
  logger = set_logger("fractal_server_settings")
68
69
  logger.debug("Fractal Settings:")
69
70
  for key, value in settings.dict().items():
70
- if any(s in key.upper() for s in ["PASSWORD", "SECRET"]):
71
+ if any(s in key.upper() for s in ["PASSWORD", "SECRET", "KEY"]):
71
72
  value = "*****"
72
73
  logger.debug(f" {key}: {value}")
73
74
  reset_logger_handlers(logger)
@@ -77,7 +78,7 @@ def check_settings() -> None:
77
78
  async def lifespan(app: FastAPI):
78
79
  app.state.jobsV2 = []
79
80
  logger = set_logger("fractal_server.lifespan")
80
- logger.info("Start application startup")
81
+ logger.info(f"[startup] START (fractal-server {__VERSION__})")
81
82
  check_settings()
82
83
  settings = Inject(get_settings)
83
84
 
@@ -88,31 +89,31 @@ async def lifespan(app: FastAPI):
88
89
  app.state.fractal_ssh_list = FractalSSHList()
89
90
 
90
91
  logger.info(
91
- "Added empty FractalSSHList to app.state "
92
+ "[startup] Added empty FractalSSHList to app.state "
92
93
  f"(id={id(app.state.fractal_ssh_list)})."
93
94
  )
94
95
  else:
95
96
  app.state.fractal_ssh_list = None
96
97
 
97
98
  config_uvicorn_loggers()
98
- logger.info("End application startup")
99
+ logger.info("[startup] END")
99
100
  reset_logger_handlers(logger)
100
101
 
101
102
  yield
102
103
 
103
104
  logger = get_logger("fractal_server.lifespan")
104
- logger.info("Start application shutdown")
105
+ logger.info("[teardown] START")
105
106
 
106
107
  if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
107
108
  logger.info(
108
- "Close FractalSSH connections "
109
+ "[teardown] Close FractalSSH connections "
109
110
  f"(current size: {app.state.fractal_ssh_list.size})."
110
111
  )
111
112
 
112
113
  app.state.fractal_ssh_list.close_all()
113
114
 
114
115
  logger.info(
115
- f"Current worker with pid {os.getpid()} is shutting down. "
116
+ f"[teardown] Current worker with pid {os.getpid()} is shutting down. "
116
117
  f"Current jobs: {app.state.jobsV2=}"
117
118
  )
118
119
  if _backend_supports_shutdown(settings.FRACTAL_RUNNER_BACKEND):
@@ -128,9 +129,11 @@ async def lifespan(app: FastAPI):
128
129
  f"Original error: {e}"
129
130
  )
130
131
  else:
131
- logger.info("Shutdown not available for this backend runner.")
132
+ logger.info(
133
+ "[teardown] Shutdown not available for this backend runner."
134
+ )
132
135
 
133
- logger.info("End application shutdown")
136
+ logger.info("[teardown] END")
134
137
  reset_logger_handlers(logger)
135
138
 
136
139
 
@@ -1,16 +1,11 @@
1
- import asyncio
2
1
  from logging.config import fileConfig
3
2
 
4
3
  from alembic import context
5
- from sqlalchemy.engine import Connection
6
4
  from sqlmodel import SQLModel
7
5
 
8
- from fractal_server.config import get_settings
9
6
  from fractal_server.migrations.naming_convention import NAMING_CONVENTION
10
- from fractal_server.syringe import Inject
11
7
 
12
- # this is the Alembic Config object, which provides
13
- # access to the values within the .ini file in use.
8
+ # Alembic Config object (provides access to the values within the .ini file)
14
9
  config = context.config
15
10
 
16
11
 
@@ -20,77 +15,35 @@ if config.config_file_name is not None:
20
15
  fileConfig(config.config_file_name)
21
16
 
22
17
 
23
- # add your model's MetaData object here
24
- # for 'autogenerate' support
25
- # from myapp import mymodel
26
- # target_metadata = mymodel.Base.metadata
27
18
  target_metadata = SQLModel.metadata
28
19
  target_metadata.naming_convention = NAMING_CONVENTION
29
- # Importing `fractal_server.app.models` after defining
20
+ # Importing `fractal_server.app.models` *after* defining
30
21
  # `SQLModel.metadata.naming_convention` in order to apply the naming convention
31
22
  # when autogenerating migrations (see issue #1819).
32
23
  from fractal_server.app import models # noqa
33
24
 
34
- # other values from the config, defined by the needs of env.py,
35
- # can be acquired:
36
- # my_important_option = config.get_main_option("my_important_option")
37
- # ... etc.
38
-
39
-
40
- def run_migrations_offline() -> None:
41
- """Run migrations in 'offline' mode.
42
-
43
- This configures the context with just a URL
44
- and not an Engine, though an Engine is acceptable
45
- here as well. By skipping the Engine creation
46
- we don't even need a DBAPI to be available.
47
-
48
- Calls to context.execute() here emit the given string to the
49
- script output.
50
25
 
26
+ def run_migrations_online() -> None:
51
27
  """
52
- settings = Inject(get_settings)
53
- settings.check_db()
54
- context.configure(
55
- url=settings.DATABASE_ASYNC_URL,
56
- target_metadata=target_metadata,
57
- literal_binds=True,
58
- dialect_opts={"paramstyle": "named"},
59
- render_as_batch=True,
60
- )
61
-
62
- with context.begin_transaction():
63
- context.run_migrations()
64
-
65
-
66
- def do_run_migrations(connection: Connection) -> None:
67
- context.configure(
68
- connection=connection,
69
- target_metadata=target_metadata,
70
- render_as_batch=True,
71
- )
72
-
73
- with context.begin_transaction():
74
- context.run_migrations()
75
-
76
-
77
- async def run_migrations_online() -> None:
78
- """Run migrations in 'online' mode.
28
+ Run migrations in 'online' mode.
79
29
 
80
30
  In this scenario we need to create an Engine
81
31
  and associate a connection with the context.
82
-
83
32
  """
84
33
  from fractal_server.app.db import DB
85
34
 
86
- engine = DB.engine_async()
87
- async with engine.connect() as connection:
88
- await connection.run_sync(do_run_migrations)
35
+ engine = DB.engine_sync()
36
+ with engine.connect() as connection:
37
+ context.configure(
38
+ connection=connection,
39
+ target_metadata=target_metadata,
40
+ render_as_batch=True,
41
+ )
42
+
43
+ with context.begin_transaction():
44
+ context.run_migrations()
89
45
 
90
- await engine.dispose()
46
+ engine.dispose()
91
47
 
92
48
 
93
- if context.is_offline_mode():
94
- run_migrations_offline()
95
- else:
96
- asyncio.run(run_migrations_online())
49
+ run_migrations_online()
@@ -7,9 +7,5 @@ COLLECTION_FREEZE_FILENAME = "collection_freeze.txt"
7
7
  FORBIDDEN_DEPENDENCY_STRINGS = ["github.com"]
8
8
 
9
9
 
10
- def get_collection_path(base: Path) -> Path:
11
- return base / COLLECTION_FILENAME
12
-
13
-
14
10
  def get_log_path(base: Path) -> Path:
15
11
  return base / COLLECTION_LOG_FILENAME
@@ -15,6 +15,7 @@ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
15
  from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
16
16
  from fractal_server.app.schemas.v2 import WheelFile
17
17
  from fractal_server.app.schemas.v2.manifest import ManifestV2
18
+ from fractal_server.logger import reset_logger_handlers
18
19
  from fractal_server.logger import set_logger
19
20
  from fractal_server.tasks.utils import get_log_path
20
21
  from fractal_server.tasks.v2.local._utils import check_task_files_exist
@@ -80,7 +81,7 @@ def collect_local(
80
81
  return
81
82
 
82
83
  # Log some info
83
- logger.debug("START")
84
+ logger.info("START")
84
85
  for key, value in task_group.model_dump().items():
85
86
  logger.debug(f"task_group.{key}: {value}")
86
87
 
@@ -101,7 +102,7 @@ def collect_local(
101
102
  try:
102
103
  # Create task_group.path folder
103
104
  Path(task_group.path).mkdir(parents=True)
104
- logger.debug(f"Created {task_group.path}")
105
+ logger.info(f"Created {task_group.path}")
105
106
 
106
107
  # Write wheel file and set task_group.wheel_path
107
108
  if wheel_file is not None:
@@ -109,9 +110,7 @@ def collect_local(
109
110
  wheel_path = (
110
111
  Path(task_group.path) / wheel_file.filename
111
112
  ).as_posix()
112
- logger.debug(
113
- f"Write wheel-file contents into {wheel_path}"
114
- )
113
+ logger.info(f"Write wheel-file contents into {wheel_path}")
115
114
  with open(wheel_path, "wb") as f:
116
115
  f.write(wheel_file.contents)
117
116
  task_group.wheel_path = wheel_path
@@ -256,12 +255,14 @@ def collect_local(
256
255
  )
257
256
 
258
257
  # Finalize (write metadata to DB)
259
- logger.debug("finalising - START")
258
+ logger.info("finalising - START")
260
259
  activity.status = TaskGroupActivityStatusV2.OK
261
260
  activity.timestamp_ended = get_timestamp()
262
261
  activity = add_commit_refresh(obj=activity, db=db)
263
- logger.debug("finalising - END")
264
- logger.debug("END")
262
+ logger.info("finalising - END")
263
+ logger.info("END")
264
+
265
+ reset_logger_handlers(logger)
265
266
 
266
267
  except Exception as collection_e:
267
268
  # Delete corrupted package dir
@@ -14,6 +14,7 @@ from fractal_server.app.models.v2 import TaskGroupV2
14
14
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
15
  from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
16
16
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
17
+ from fractal_server.logger import reset_logger_handlers
17
18
  from fractal_server.logger import set_logger
18
19
  from fractal_server.tasks.utils import FORBIDDEN_DEPENDENCY_STRINGS
19
20
  from fractal_server.tasks.utils import get_log_path
@@ -217,6 +218,8 @@ def deactivate_local(
217
218
  activity.timestamp_ended = get_timestamp()
218
219
  activity = add_commit_refresh(obj=activity, db=db)
219
220
 
221
+ reset_logger_handlers(logger)
222
+
220
223
  except Exception as e:
221
224
  fail_and_cleanup(
222
225
  task_group=task_group,
@@ -13,6 +13,7 @@ from fractal_server.app.models.v2 import TaskGroupActivityV2
13
13
  from fractal_server.app.models.v2 import TaskGroupV2
14
14
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
15
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
16
+ from fractal_server.logger import reset_logger_handlers
16
17
  from fractal_server.logger import set_logger
17
18
  from fractal_server.tasks.utils import get_log_path
18
19
  from fractal_server.tasks.v2.utils_background import get_current_log
@@ -134,6 +135,8 @@ def reactivate_local(
134
135
  task_group = add_commit_refresh(obj=task_group, db=db)
135
136
  logger.debug("END")
136
137
 
138
+ reset_logger_handlers(logger)
139
+
137
140
  except Exception as reactivate_e:
138
141
  # Delete corrupted venv_path
139
142
  try:
@@ -14,6 +14,7 @@ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
14
14
  from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
15
15
  from fractal_server.app.schemas.v2 import WheelFile
16
16
  from fractal_server.app.schemas.v2.manifest import ManifestV2
17
+ from fractal_server.logger import reset_logger_handlers
17
18
  from fractal_server.logger import set_logger
18
19
  from fractal_server.ssh._fabric import FractalSSH
19
20
  from fractal_server.tasks.v2.ssh._utils import _customize_and_run_template
@@ -85,7 +86,7 @@ def collect_ssh(
85
86
  return
86
87
 
87
88
  # Log some info
88
- logger.debug("START")
89
+ logger.info("START")
89
90
  for key, value in task_group.model_dump().items():
90
91
  logger.debug(f"task_group.{key}: {value}")
91
92
 
@@ -137,7 +138,7 @@ def collect_ssh(
137
138
  Path(task_group.path) / wheel_filename
138
139
  ).as_posix()
139
140
  tmp_wheel_path = (Path(tmpdir) / wheel_filename).as_posix()
140
- logger.debug(
141
+ logger.info(
141
142
  f"Write wheel-file contents into {tmp_wheel_path}"
142
143
  )
143
144
  with open(tmp_wheel_path, "wb") as f:
@@ -171,7 +172,7 @@ def collect_ssh(
171
172
  logger_name=LOGGER_NAME,
172
173
  )
173
174
 
174
- logger.debug("installing - START")
175
+ logger.info("installing - START")
175
176
 
176
177
  # Set status to ONGOING and refresh logs
177
178
  activity.status = TaskGroupActivityStatusV2.ONGOING
@@ -286,14 +287,13 @@ def collect_ssh(
286
287
  )
287
288
 
288
289
  # Finalize (write metadata to DB)
289
- logger.debug("finalising - START")
290
+ logger.info("finalising - START")
290
291
  activity.status = TaskGroupActivityStatusV2.OK
291
292
  activity.timestamp_ended = get_timestamp()
292
293
  activity = add_commit_refresh(obj=activity, db=db)
293
- logger.debug("finalising - END")
294
- logger.debug("END")
295
-
296
- logger.debug("END")
294
+ logger.info("finalising - END")
295
+ logger.info("END")
296
+ reset_logger_handlers(logger)
297
297
 
298
298
  except Exception as collection_e:
299
299
  # Delete corrupted package dir
@@ -14,6 +14,7 @@ from fractal_server.app.models.v2 import TaskGroupV2
14
14
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
15
  from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
16
16
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
17
+ from fractal_server.logger import reset_logger_handlers
17
18
  from fractal_server.logger import set_logger
18
19
  from fractal_server.ssh._fabric import FractalSSH
19
20
  from fractal_server.tasks.utils import FORBIDDEN_DEPENDENCY_STRINGS
@@ -252,6 +253,8 @@ def deactivate_ssh(
252
253
  activity.timestamp_ended = get_timestamp()
253
254
  activity = add_commit_refresh(obj=activity, db=db)
254
255
 
256
+ reset_logger_handlers(logger)
257
+
255
258
  except Exception as e:
256
259
  fail_and_cleanup(
257
260
  task_group=task_group,
@@ -12,6 +12,7 @@ from fractal_server.app.models.v2 import TaskGroupActivityV2
12
12
  from fractal_server.app.models.v2 import TaskGroupV2
13
13
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
14
14
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
15
+ from fractal_server.logger import reset_logger_handlers
15
16
  from fractal_server.logger import set_logger
16
17
  from fractal_server.ssh._fabric import FractalSSH
17
18
  from fractal_server.tasks.utils import get_log_path
@@ -69,7 +70,7 @@ def reactivate_ssh(
69
70
  return
70
71
 
71
72
  # Log some info
72
- logger.debug("START")
73
+ logger.info("START")
73
74
  for key, value in task_group.model_dump().items():
74
75
  logger.debug(f"task_group.{key}: {value}")
75
76
 
@@ -152,28 +153,30 @@ def reactivate_ssh(
152
153
  # Create remote directory for scripts
153
154
  fractal_ssh.mkdir(folder=script_dir_remote)
154
155
 
155
- logger.debug("start - create venv")
156
+ logger.info("start - create venv")
156
157
  _customize_and_run_template(
157
158
  template_filename="1_create_venv.sh",
158
159
  **common_args,
159
160
  )
160
- logger.debug("end - create venv")
161
+ logger.info("end - create venv")
161
162
  activity.log = get_current_log(log_file_path)
162
163
  activity = add_commit_refresh(obj=activity, db=db)
163
164
 
164
- logger.debug("start - install from pip freeze")
165
+ logger.info("start - install from pip freeze")
165
166
  _customize_and_run_template(
166
167
  template_filename="6_pip_install_from_freeze.sh",
167
168
  **common_args,
168
169
  )
169
- logger.debug("end - install from pip freeze")
170
+ logger.info("end - install from pip freeze")
170
171
  activity.log = get_current_log(log_file_path)
171
172
  activity.status = TaskGroupActivityStatusV2.OK
172
173
  activity.timestamp_ended = get_timestamp()
173
174
  activity = add_commit_refresh(obj=activity, db=db)
174
175
  task_group.active = True
175
176
  task_group = add_commit_refresh(obj=task_group, db=db)
176
- logger.debug("END")
177
+ logger.info("END")
178
+
179
+ reset_logger_handlers(logger)
177
180
 
178
181
  except Exception as reactivate_e:
179
182
  # Delete corrupted venv_path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.12.0a0
3
+ Version: 2.12.1
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -27,7 +27,7 @@ Requires-Dist: pydantic (>=1.10.8,<2)
27
27
  Requires-Dist: python-dotenv (>=1.0.0,<1.1.0)
28
28
  Requires-Dist: sqlalchemy[asyncio] (>=2.0.23,<2.1)
29
29
  Requires-Dist: sqlmodel (==0.0.21)
30
- Requires-Dist: uvicorn (==0.29.0)
30
+ Requires-Dist: uvicorn (>=0.29.0,<0.35.0)
31
31
  Requires-Dist: uvicorn-worker (==0.2.0)
32
32
  Project-URL: Documentation, https://fractal-analytics-platform.github.io/fractal-server
33
33
  Project-URL: Repository, https://github.com/fractal-analytics-platform/fractal-server