1818
1919import click
2020from dandischema import models
21+ import dandischema .consts
22+ from packaging .version import Version as PackagingVersion
2123from pydantic import BaseModel , Field , PrivateAttr
2224import requests
2325import tenacity
@@ -646,12 +648,21 @@ def create_dandiset(
646648
647649 def check_schema_version (self , schema_version : str | None = None ) -> None :
648650 """
649- Confirms that the server is using the same version of the DANDI schema
650- as the client. If it is not, a `SchemaVersionError` is raised.
651+ Confirms that the given schema version at the client is "compatible" with the server.
651652
652- :param schema_version: the schema version to confirm that the server
653- uses; if not set, the schema version for the installed
654- ``dandischema`` library is used
653+ Compatibility here means that the server's schema version can be either
654+
655+ - lower than client has, but within the same MAJOR.MINOR component of the version
656+ number for 0.x series, and same MAJOR version for/after 1.x series;
657+ - the same;
658+ - higher than the client has, but only if the client's schema version is listed
659+ among the server's `allowed_schema_versions` (as returned by the `/info` API endpoint),
660+ or if not there -- `dandischema.consts.ALLOWED_INPUT_SCHEMAS` is consulted.
661+
662+ If neither of above, a `SchemaVersionError` is raised.
663+
664+ :param schema_version: the schema version to be confirmed for compatibility with the server;
665+ if not set, the schema version for the installed ``dandischema`` library is used.
655666 """
656667 if schema_version is None :
657668 schema_version = models .get_schema_version ()
@@ -662,11 +673,48 @@ def check_schema_version(self, schema_version: str | None = None) -> None:
662673 "Server did not provide schema_version in /info/;"
663674 f" returned { server_info !r} "
664675 )
665- if server_schema_version != schema_version :
676+ server_ver , our_ver = PackagingVersion (server_schema_version ), PackagingVersion (
677+ schema_version
678+ )
679+ if server_ver > our_ver :
680+ # TODO: potentially adjust here if name would be different: see
681+ # https://github.com/dandi/dandi-archive/issues/2624
682+ allowed_schema_versions = server_info .get (
683+ "allowed_schema_versions" , dandischema .consts .ALLOWED_INPUT_SCHEMAS
684+ )
685+ if schema_version not in allowed_schema_versions :
686+ raise SchemaVersionError (
687+ f"Server uses schema version { server_schema_version } ;"
688+ f" client only supports prior { schema_version } and it"
689+ f" is not among any of the allowed upgradable schema versions"
690+ f" ({ ', ' .join (allowed_schema_versions )} ) . You may need to"
691+ " upgrade dandi and/or dandischema."
692+ )
693+
694+ # TODO: check current server behavior which is likely to just not care!
695+ # So that is where server might need to provide support for upgrades upon
696+ # providing metadata.
697+ elif (
698+ server_ver .major == 0 and server_ver .release [:2 ] != our_ver .release [:2 ]
699+ ) or (
700+ server_ver .major != our_ver .major
701+ ): # MAJOR, MINOR within 0.x.y and MAJOR within 1.x.y
666702 raise SchemaVersionError (
667- f"Server requires schema version { server_schema_version } ;"
668- f" client only supports { schema_version } . You may need to"
669- " upgrade dandi and/or dandischema."
703+ f"Server uses older incompatible schema version { server_schema_version } ;"
704+ f" client supports { schema_version } ."
705+ )
706+ elif server_ver < our_ver :
707+ # Compatible older server version -- all good, but inform the user
708+ # TODO: potentially downgrade the record to match the schema,
709+ # see https://github.com/dandi/dandi-schema/issues/343
710+ lgr .warning (
711+ "Server uses schema version %s older than client's %s (dandischema library %s). "
712+ "Server might fail to validate such assets and you might not be able to "
713+ "publish this dandiset until server is upgraded. "
714+ "Alternatively, you may downgrade dandischema and reupload." ,
715+ server_ver ,
716+ our_ver ,
717+ dandischema .__version__ ,
670718 )
671719
672720 def get_asset (self , asset_id : str ) -> BaseRemoteAsset :
0 commit comments