1111
1212from typing import Sequence , Optional , Mapping
1313
14- from .versions import VersionRaw , guess_version
14+ from .versions import VersionRaw , guess_version , version_setup
1515from .meta import Meta , MetaRaw , MetaFactory
16- from .errors import PinsError
16+ from .errors import PinsError , PinsVersionError
1717from .drivers import load_data , save_data , load_file , default_title
1818from .utils import inform , warn_deprecated , ExtendMethodDoc
1919from .config import get_allow_rsc_short_name
@@ -90,16 +90,14 @@ def pin_versions(self, name: str, as_df: bool = True) -> Sequence[VersionRaw]:
9090 Pin name.
9191
9292 """
93- if not self .versioned :
94- raise NotImplementedError (
95- "Cannot show versions for a board type that does not support versioning."
96- )
9793
9894 if not self .pin_exists (name ):
9995 raise PinsError ("Cannot check version, since pin %s does not exist" % name )
10096
97+ detail = isinstance (self , BoardRsConnect )
98+
10199 versions_raw = self .fs .ls (
102- self .construct_path ([self .path_to_pin (name )]), detail = False
100+ self .construct_path ([self .path_to_pin (name )]), detail = detail
103101 )
104102
105103 # get a list of Version(Raw) objects
@@ -111,7 +109,6 @@ def pin_versions(self, name: str, as_df: bool = True) -> Sequence[VersionRaw]:
111109 # sort them, with latest last
112110 sorted_versions = self .sort_pin_versions (all_versions )
113111
114- # TODO(defer): this deviates from R pins, which returns a df by default
115112 if as_df :
116113 import pandas as pd
117114
@@ -241,11 +238,6 @@ def _pin_store(
241238 created : Optional [datetime ] = None ,
242239 ) -> Meta :
243240
244- if not self .versioned :
245- raise NotImplementedError (
246- "Can only write pins with boards that support versioning."
247- )
248-
249241 if type == "feather" :
250242 warn_deprecated (
251243 'Writing pin type "feather" is unsupported. Switching type to "arrow".'
@@ -360,11 +352,6 @@ def pin_write(
360352 part of the pin version name.
361353 """
362354
363- if not self .versioned :
364- raise NotImplementedError (
365- "Can only write pins with boards that support versioning."
366- )
367-
368355 if type == "file" :
369356 raise NotImplementedError (
370357 ".pin_write() does not support type='file'. "
@@ -463,10 +450,6 @@ def pin_version_delete(self, name: str, version: str):
463450 version:
464451 Version identifier.
465452 """
466- if not self .versioned :
467- raise NotImplementedError (
468- "Can only write pins with boards that support versioning."
469- )
470453
471454 pin_name = self .path_to_pin (name )
472455
@@ -616,7 +599,7 @@ def pin_browse(self, name, version=None, local=False):
616599 def validate_pin_name (self , name : str ) -> None :
617600 """Raise an error if a pin name is not valid."""
618601
619- if "/" in name :
602+ if name and "/" in name :
620603 raise ValueError (f"Invalid pin name: { name } " )
621604 elif name in self .reserved_pin_names :
622605 raise ValueError (f"The pin name '{ name } ' is reserved for internal use." )
@@ -654,13 +637,41 @@ def prepare_pin_version(
654637 versioned : Optional [bool ] = None ,
655638 created : Optional [datetime ] = None ,
656639 object_name : Optional [str ] = None ,
640+ ):
641+ meta = self ._create_meta (
642+ pin_dir_path ,
643+ x ,
644+ name ,
645+ type ,
646+ title ,
647+ description ,
648+ metadata ,
649+ versioned ,
650+ created ,
651+ object_name ,
652+ )
653+
654+ # handle unversioned boards
655+ version_setup (self , name , meta .version , versioned )
656+
657+ return meta
658+
659+ def _create_meta (
660+ self ,
661+ pin_dir_path ,
662+ x ,
663+ name : Optional [str ] = None ,
664+ type : Optional [str ] = None ,
665+ title : Optional [str ] = None ,
666+ description : Optional [str ] = None ,
667+ metadata : Optional [Mapping ] = None ,
668+ versioned : Optional [bool ] = None ,
669+ created : Optional [datetime ] = None ,
670+ object_name : Optional [str ] = None ,
657671 ):
658672 if name is None :
659673 raise NotImplementedError ("Name must be specified." )
660674
661- if versioned is False :
662- raise NotImplementedError ("Only writing versioned pins supported." )
663-
664675 if type is None :
665676 raise NotImplementedError ("Type argument is required." )
666677
@@ -770,15 +781,6 @@ def __init__(self, *args, pin_paths: dict, **kwargs):
770781
771782 self .pin_paths = pin_paths
772783
773- # def pin_read(self, *args, **kwargs):
774- # if not get_feature_preview():
775- # raise NotImplementedError(
776- # "pin_read with BoardManual is currently unimplemented. "
777- # "See https://github.com/machow/pins-python/issues/59."
778- # )
779-
780- # return super().pin_read(*args, **kwargs)
781-
782784 @ExtendMethodDoc
783785 def pin_list (self ):
784786 return list (self .pin_paths )
@@ -889,7 +891,9 @@ def pin_list(self):
889891 return names
890892
891893 @ExtendMethodDoc
892- def pin_write (self , * args , access_type = None , ** kwargs ):
894+ def pin_write (
895+ self , * args , access_type = None , versioned : Optional [bool ] = None , ** kwargs
896+ ):
893897 """Write a pin.
894898
895899 Extends parent method in the following ways:
@@ -915,6 +919,28 @@ def pin_write(self, *args, access_type=None, **kwargs):
915919 " can write to it."
916920 )
917921
922+ # attempt to make the least number of API calls possible
923+ if versioned or versioned is None and self .versioned :
924+ # arbitrary number greater than 1
925+ n_versions_before = 100
926+ else :
927+ try :
928+ versions_df = self .pin_versions (pin_name , as_df = True )
929+ versions = versions_df ["version" ].to_list ()
930+ n_versions_before = len (versions )
931+ except PinsError :
932+ # pin does not exist
933+ n_versions_before = 0
934+
935+ if versioned is None :
936+ versioned = True if n_versions_before > 1 else self .versioned
937+
938+ if versioned is False and n_versions_before > 1 :
939+ raise PinsVersionError (
940+ "Pin is versioned, but you have requested a write without versions."
941+ "To un-version a pin, you must delete it"
942+ )
943+
918944 # run parent function ---
919945 meta = f_super (* args , ** kwargs )
920946
@@ -928,6 +954,12 @@ def pin_write(self, *args, access_type=None, **kwargs):
928954 access_type = access_type or content ["access_type" ],
929955 )
930956
957+ # clean up non-active pins in the case of an unversioned board
958+ # a pin existed before the latest pin
959+ if versioned is False and n_versions_before == 1 :
960+ _log .info (f"Replacing version '{ versions } ' with '{ meta .version .version } '" )
961+ self .pin_version_delete (pin_name , versions [0 ])
962+
931963 return meta
932964
933965 @ExtendMethodDoc
@@ -1067,7 +1099,7 @@ def prepare_pin_version(self, pin_dir_path, x, name: "str | None", *args, **kwar
10671099
10681100 # TODO(compat): py pins always uses the short name, R pins uses w/e the
10691101 # user passed, but guessing people want the long name?
1070- meta = super ().prepare_pin_version (pin_dir_path , x , short_name , * args , ** kwargs )
1102+ meta = super ()._create_meta (pin_dir_path , x , short_name , * args , ** kwargs )
10711103 meta .name = name
10721104
10731105 # copy in files needed by index.html ----------------------------------
0 commit comments