@@ -711,3 +711,120 @@ def args_in_kwargs(args: Sequence[str], kwargs: dict[str, Any]) -> bool:
711
711
return any (
712
712
kwargs .get (arg ) is not None and kwargs .get (arg ) is not False for arg in args
713
713
)
714
+
715
+
716
+ def sequence_join (
717
+ value : Any ,
718
+ separator : str = "/" ,
719
+ size : int | Sequence [int ] | None = None ,
720
+ ndim : int = 1 ,
721
+ name : str | None = None ,
722
+ ) -> str | list [str ] | None | Any :
723
+ """
724
+ Join a sequence of values into a string separated by a separator.
725
+
726
+ A 1-D sequence will be joined into a single string. A 2-D sequence will be joined
727
+ into a list of strings. Non-sequence values will be returned as is.
728
+
729
+ Parameters
730
+ ----------
731
+ value
732
+ The 1-D or 2-D sequence of values to join.
733
+ separator
734
+ The separator to join the values.
735
+ size
736
+ Expected size of the 1-D sequence. It can be either an integer or a sequence of
737
+ integers. If an integer, it is the expected size of the 1-D sequence. If it is a
738
+ sequence, it is the allowed sizes of the 1-D sequence.
739
+ ndim
740
+ The expected maximum number of dimensions of the sequence.
741
+ name
742
+ The name of the parameter to be used in the error message.
743
+
744
+ Returns
745
+ -------
746
+ joined_value
747
+ The joined string or list of strings.
748
+
749
+ Examples
750
+ --------
751
+ >>> sequence_join("1/2/3/4")
752
+ '1/2/3/4'
753
+ >>> sequence_join(None)
754
+ >>> sequence_join(True)
755
+ True
756
+ >>> sequence_join(False)
757
+ False
758
+
759
+ >>> sequence_join([])
760
+ Traceback (most recent call last):
761
+ ...
762
+ pygmt.exceptions.GMTInvalidInput: Expected a sequence but got an empty sequence.
763
+
764
+ >>> sequence_join([1, 2, 3, 4])
765
+ '1/2/3/4'
766
+ >>> sequence_join([1, 2, 3, 4], separator=",")
767
+ '1,2,3,4'
768
+ >>> sequence_join([1, 2, 3, 4], separator="/", size=4)
769
+ '1/2/3/4'
770
+ >>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4])
771
+ '1/2/3/4'
772
+ >>> sequence_join([1, 2, 3, 4], separator="/", size=[2, 4], ndim=2)
773
+ '1/2/3/4'
774
+ >>> sequence_join([1, 2, 3, 4], separator="/", size=2)
775
+ Traceback (most recent call last):
776
+ ...
777
+ pygmt.exceptions.GMTInvalidInput: Expected a sequence of 2 values, but got 4 values.
778
+ >>> sequence_join([1, 2, 3, 4, 5], separator="/", size=[2, 4], name="parname")
779
+ Traceback (most recent call last):
780
+ ...
781
+ pygmt.exceptions.GMTInvalidInput: Parameter 'parname': Expected ...
782
+
783
+ >>> sequence_join([[1, 2], [3, 4]], separator="/")
784
+ Traceback (most recent call last):
785
+ ...
786
+ pygmt.exceptions.GMTInvalidInput: Expected a 1-D ..., but a 2-D sequence is given.
787
+ >>> sequence_join([[1, 2], [3, 4]], separator="/", ndim=2)
788
+ ['1/2', '3/4']
789
+ >>> sequence_join([[1, 2], [3, 4]], separator="/", size=2, ndim=2)
790
+ ['1/2', '3/4']
791
+ >>> sequence_join([[1, 2], [3, 4]], separator="/", size=4, ndim=2)
792
+ Traceback (most recent call last):
793
+ ...
794
+ pygmt.exceptions.GMTInvalidInput: Expected a sequence of 4 values.
795
+ >>> sequence_join([[1, 2], [3, 4]], separator="/", size=[2, 4], ndim=2)
796
+ ['1/2', '3/4']
797
+ """
798
+ # Return the original value if it is not a sequence (e.g., None, bool, or str).
799
+ if not is_nonstr_iter (value ):
800
+ return value
801
+ # Now it must be a sequence.
802
+
803
+ # Change size to a list to simplify the checks.
804
+ size = [size ] if isinstance (size , int ) else size
805
+ errmsg = {
806
+ "name" : f"Parameter '{ name } ': " if name else "" ,
807
+ "sizes" : ", " .join (str (s ) for s in size ) if size is not None else "" ,
808
+ }
809
+
810
+ if len (value ) == 0 :
811
+ msg = f"{ errmsg ['name' ]} Expected a sequence but got an empty sequence."
812
+ raise GMTInvalidInput (msg )
813
+
814
+ if not is_nonstr_iter (value [0 ]): # 1-D sequence.
815
+ if size is not None and len (value ) not in size :
816
+ msg = (
817
+ f"{ errmsg ['name' ]} Expected a sequence of { errmsg ['sizes' ]} values, "
818
+ f"but got { len (value )} values."
819
+ )
820
+ raise GMTInvalidInput (msg )
821
+ return separator .join (str (v ) for v in value )
822
+
823
+ # Now it must be a 2-D sequence.
824
+ if ndim == 1 :
825
+ msg = f"{ errmsg ['name' ]} Expected a 1-D sequence, but a 2-D sequence is given."
826
+ raise GMTInvalidInput (msg )
827
+ if size is not None and any (len (i ) not in size for i in value ):
828
+ msg = f"{ errmsg ['name' ]} Expected a sequence of { errmsg ['sizes' ]} values."
829
+ raise GMTInvalidInput (msg )
830
+ return [separator .join (str (j ) for j in sub ) for sub in value ]
0 commit comments