forked from Rust-related/RustPython
Merge pull request #1873 from youknowone/fromargs-generic
merge SplitArgs + Generic support for FromArgs
This commit is contained in:
@@ -16,14 +16,11 @@ enum ParameterKind {
|
||||
|
||||
impl ParameterKind {
|
||||
fn from_ident(ident: &Ident) -> Option<ParameterKind> {
|
||||
if ident == "positional_only" {
|
||||
Some(ParameterKind::PositionalOnly)
|
||||
} else if ident == "positional_or_keyword" {
|
||||
Some(ParameterKind::PositionalOrKeyword)
|
||||
} else if ident == "keyword_only" {
|
||||
Some(ParameterKind::KeywordOnly)
|
||||
} else {
|
||||
None
|
||||
match ident.to_string().as_str() {
|
||||
"positional_only" => Some(ParameterKind::PositionalOnly),
|
||||
"positional_or_keyword" => Some(ParameterKind::PositionalOrKeyword),
|
||||
"keyword_only" => Some(ParameterKind::KeywordOnly),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +147,13 @@ fn generate_field(field: &Field) -> Result<TokenStream2, Diagnostic> {
|
||||
};
|
||||
|
||||
let name = &field.ident;
|
||||
if let Some(name) = name {
|
||||
if name.to_string().starts_with("_phantom") {
|
||||
return Ok(quote! {
|
||||
#name: std::marker::PhantomData,
|
||||
});
|
||||
}
|
||||
}
|
||||
let middle = quote! {
|
||||
.map(|x| ::rustpython_vm::pyobject::TryFromObject::try_from_object(vm, x)).transpose()?
|
||||
};
|
||||
@@ -210,8 +214,9 @@ pub fn impl_from_args(input: DeriveInput) -> Result<TokenStream2, Diagnostic> {
|
||||
};
|
||||
|
||||
let name = input.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let output = quote! {
|
||||
impl ::rustpython_vm::function::FromArgs for #name {
|
||||
impl #impl_generics ::rustpython_vm::function::FromArgs for #name #ty_generics #where_clause {
|
||||
fn from_args(
|
||||
vm: &::rustpython_vm::VirtualMachine,
|
||||
args: &mut ::rustpython_vm::function::PyFuncArgs
|
||||
|
||||
@@ -4,16 +4,15 @@ use std::convert::TryFrom;
|
||||
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use super::objbyteinner::{
|
||||
ByteInnerExpandtabsOptions, ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
|
||||
ByteInnerSplitOptions, ByteInnerSplitlinesOptions, ByteInnerTranslateOptions, ByteOr,
|
||||
PyByteInner,
|
||||
ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerSplitOptions,
|
||||
ByteInnerTranslateOptions, ByteOr, PyByteInner,
|
||||
};
|
||||
use super::objint::PyIntRef;
|
||||
use super::objiter;
|
||||
use super::objslice::PySliceRef;
|
||||
use super::objstr::{PyString, PyStringRef};
|
||||
use super::objtype::PyClassRef;
|
||||
use super::pystr::PyCommonString;
|
||||
use super::pystr::{self, PyCommonString};
|
||||
use crate::cformat::CFormatString;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::obj::objstr::do_cformat_string;
|
||||
@@ -440,12 +439,12 @@ impl PyByteArray {
|
||||
}
|
||||
|
||||
#[pymethod(name = "expandtabs")]
|
||||
fn expandtabs(&self, options: ByteInnerExpandtabsOptions) -> PyByteArray {
|
||||
fn expandtabs(&self, options: pystr::ExpandTabsArgs) -> PyByteArray {
|
||||
self.borrow_value().expandtabs(options).into()
|
||||
}
|
||||
|
||||
#[pymethod(name = "splitlines")]
|
||||
fn splitlines(&self, options: ByteInnerSplitlinesOptions, vm: &VirtualMachine) -> PyResult {
|
||||
fn splitlines(&self, options: pystr::SplitLinesArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let as_bytes = self
|
||||
.borrow_value()
|
||||
.splitlines(options)
|
||||
|
||||
@@ -14,7 +14,7 @@ use super::objnone::PyNoneRef;
|
||||
use super::objsequence::PySliceableSequence;
|
||||
use super::objslice::PySliceRef;
|
||||
use super::objstr::{self, PyString, PyStringRef};
|
||||
use super::pystr::{self, PyCommonString, StringRange};
|
||||
use super::pystr::{self, PyCommonString, PyCommonStringWrapper, StringRange};
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::pyhash;
|
||||
use crate::pyobject::{
|
||||
@@ -255,43 +255,7 @@ impl ByteInnerTranslateOptions {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct ByteInnerSplitOptions {
|
||||
#[pyarg(positional_or_keyword, default = "None")]
|
||||
sep: Option<PyByteInner>,
|
||||
#[pyarg(positional_or_keyword, default = "-1")]
|
||||
maxsplit: isize,
|
||||
}
|
||||
|
||||
impl ByteInnerSplitOptions {
|
||||
pub fn get_value(self, vm: &VirtualMachine) -> PyResult<(Option<Vec<u8>>, isize)> {
|
||||
let sep = if let Some(s) = self.sep {
|
||||
let sep = s.elements;
|
||||
if sep.is_empty() {
|
||||
return Err(vm.new_value_error("empty separator".to_owned()));
|
||||
}
|
||||
Some(sep)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((sep, self.maxsplit))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct ByteInnerExpandtabsOptions {
|
||||
#[pyarg(positional_or_keyword, optional = true)]
|
||||
tabsize: OptionalArg<PyIntRef>,
|
||||
}
|
||||
|
||||
impl ByteInnerExpandtabsOptions {
|
||||
pub fn get_value(self) -> usize {
|
||||
match self.tabsize.into_option() {
|
||||
Some(int) => int.as_bigint().to_usize().unwrap_or(0),
|
||||
None => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type ByteInnerSplitOptions = pystr::SplitArgs<PyByteInner, [u8], u8>;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct ByteInnerSplitlinesOptions {
|
||||
@@ -970,19 +934,13 @@ impl PyByteInner {
|
||||
where
|
||||
F: Fn(&[u8], &VirtualMachine) -> PyObjectRef,
|
||||
{
|
||||
let (sep, maxsplit) = options.get_value(vm)?;
|
||||
let sep_ref = match sep {
|
||||
Some(ref v) => Some(&v[..]),
|
||||
None => None,
|
||||
};
|
||||
let elements = self.elements.py_split(
|
||||
sep_ref,
|
||||
maxsplit,
|
||||
options,
|
||||
vm,
|
||||
|v, s, vm| v.split_str(s).map(|v| convert(v, vm)).collect(),
|
||||
|v, s, n, vm| v.splitn_str(n, s).map(|v| convert(v, vm)).collect(),
|
||||
|v, n, vm| v.py_split_whitespace(n, |v| convert(v, vm)),
|
||||
);
|
||||
)?;
|
||||
Ok(vm.ctx.new_list(elements))
|
||||
}
|
||||
|
||||
@@ -995,19 +953,13 @@ impl PyByteInner {
|
||||
where
|
||||
F: Fn(&[u8], &VirtualMachine) -> PyObjectRef,
|
||||
{
|
||||
let (sep, maxsplit) = options.get_value(vm)?;
|
||||
let sep_ref = match sep {
|
||||
Some(ref v) => Some(&v[..]),
|
||||
None => None,
|
||||
};
|
||||
let mut elements = self.elements.py_split(
|
||||
sep_ref,
|
||||
maxsplit,
|
||||
options,
|
||||
vm,
|
||||
|v, s, vm| v.rsplit_str(s).map(|v| convert(v, vm)).collect(),
|
||||
|v, s, n, vm| v.rsplitn_str(n, s).map(|v| convert(v, vm)).collect(),
|
||||
|v, n, vm| v.py_rsplit_whitespace(n, |v| convert(v, vm)),
|
||||
);
|
||||
)?;
|
||||
elements.reverse();
|
||||
Ok(vm.ctx.new_list(elements))
|
||||
}
|
||||
@@ -1050,8 +1002,8 @@ impl PyByteInner {
|
||||
Ok((front, has_mid, back))
|
||||
}
|
||||
|
||||
pub fn expandtabs(&self, options: ByteInnerExpandtabsOptions) -> Vec<u8> {
|
||||
let tabsize = options.get_value();
|
||||
pub fn expandtabs(&self, options: pystr::ExpandTabsArgs) -> Vec<u8> {
|
||||
let tabsize = options.tabsize();
|
||||
let mut counter: usize = 0;
|
||||
let mut res = vec![];
|
||||
|
||||
@@ -1082,9 +1034,7 @@ impl PyByteInner {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn splitlines(&self, options: ByteInnerSplitlinesOptions) -> Vec<&[u8]> {
|
||||
let keepends = options.get_value();
|
||||
|
||||
pub fn splitlines(&self, options: pystr::SplitLinesArgs) -> Vec<&[u8]> {
|
||||
let mut res = vec![];
|
||||
|
||||
if self.elements.is_empty() {
|
||||
@@ -1093,7 +1043,7 @@ impl PyByteInner {
|
||||
|
||||
let mut prev_index = 0;
|
||||
let mut index = 0;
|
||||
let keep = if keepends { 1 } else { 0 };
|
||||
let keep = if options.keepends { 1 } else { 0 };
|
||||
let slice = &self.elements;
|
||||
|
||||
while index < slice.len() {
|
||||
@@ -1435,13 +1385,23 @@ pub fn bytes_zfill(bytes: &[u8], width: usize) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyCommonStringWrapper<[u8]> for PyByteInner {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.elements
|
||||
}
|
||||
}
|
||||
|
||||
const ASCII_WHITESPACES: [u8; 6] = [0x20, 0x09, 0x0a, 0x0c, 0x0d, 0x0b];
|
||||
|
||||
impl PyCommonString<'_, u8> for [u8] {
|
||||
impl PyCommonString<u8> for [u8] {
|
||||
fn get_slice(&self, range: std::ops::Range<usize>) -> &Self {
|
||||
&self[range]
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
Self::is_empty(self)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
Self::len(self)
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::objbyteinner::{
|
||||
ByteInnerExpandtabsOptions, ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
|
||||
ByteInnerSplitOptions, ByteInnerSplitlinesOptions, ByteInnerTranslateOptions, PyByteInner,
|
||||
ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions, ByteInnerSplitOptions,
|
||||
ByteInnerTranslateOptions, PyByteInner,
|
||||
};
|
||||
use super::objint::PyIntRef;
|
||||
use super::objiter;
|
||||
use super::objslice::PySliceRef;
|
||||
use super::objstr::{PyString, PyStringRef};
|
||||
use super::objtype::PyClassRef;
|
||||
use super::pystr::PyCommonString;
|
||||
use super::pystr::{self, PyCommonString};
|
||||
use crate::cformat::CFormatString;
|
||||
use crate::function::{OptionalArg, OptionalOption};
|
||||
use crate::obj::objstr::do_cformat_string;
|
||||
@@ -401,12 +401,12 @@ impl PyBytes {
|
||||
}
|
||||
|
||||
#[pymethod(name = "expandtabs")]
|
||||
fn expandtabs(&self, options: ByteInnerExpandtabsOptions) -> PyBytes {
|
||||
fn expandtabs(&self, options: pystr::ExpandTabsArgs) -> PyBytes {
|
||||
self.inner.expandtabs(options).into()
|
||||
}
|
||||
|
||||
#[pymethod(name = "splitlines")]
|
||||
fn splitlines(&self, options: ByteInnerSplitlinesOptions, vm: &VirtualMachine) -> PyResult {
|
||||
fn splitlines(&self, options: pystr::SplitLinesArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let as_bytes = self
|
||||
.inner
|
||||
.splitlines(options)
|
||||
|
||||
@@ -21,7 +21,7 @@ use super::objsequence::PySliceableSequence;
|
||||
use super::objslice::PySliceRef;
|
||||
use super::objtuple;
|
||||
use super::objtype::{self, PyClassRef};
|
||||
use super::pystr::{adjust_indices, PyCommonString, StringRange};
|
||||
use super::pystr::{self, adjust_indices, PyCommonString, PyCommonStringWrapper, StringRange};
|
||||
use crate::cformat::{
|
||||
CFormatPart, CFormatPreconversor, CFormatQuantity, CFormatSpec, CFormatString, CFormatType,
|
||||
CNumberType,
|
||||
@@ -168,18 +168,6 @@ struct StrArgs {
|
||||
errors: OptionalArg<PyStringRef>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct SplitLineArgs {
|
||||
#[pyarg(positional_or_keyword, optional = true)]
|
||||
keepends: OptionalArg<bool>,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct ExpandtabsArgs {
|
||||
#[pyarg(positional_or_keyword, optional = true)]
|
||||
tabsize: OptionalArg<usize>,
|
||||
}
|
||||
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyString {
|
||||
#[pyslot]
|
||||
@@ -457,26 +445,24 @@ impl PyString {
|
||||
#[pymethod]
|
||||
fn split(&self, args: SplitArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let elements = self.value.py_split(
|
||||
args.non_empty_sep(vm)?,
|
||||
args.maxsplit,
|
||||
args,
|
||||
vm,
|
||||
|v, s, vm| v.split(s).map(|s| vm.ctx.new_str(s)).collect(),
|
||||
|v, s, n, vm| v.splitn(n, s).map(|s| vm.ctx.new_str(s)).collect(),
|
||||
|v, n, vm| v.py_split_whitespace(n, |s| vm.ctx.new_str(s)),
|
||||
);
|
||||
)?;
|
||||
Ok(vm.ctx.new_list(elements))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn rsplit(&self, args: SplitArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let mut elements = self.value.py_split(
|
||||
args.non_empty_sep(vm)?,
|
||||
args.maxsplit,
|
||||
args,
|
||||
vm,
|
||||
|v, s, vm| v.rsplit(s).map(|s| vm.ctx.new_str(s)).collect(),
|
||||
|v, s, n, vm| v.rsplitn(n, s).map(|s| vm.ctx.new_str(s)).collect(),
|
||||
|v, n, vm| v.py_rsplit_whitespace(n, |s| vm.ctx.new_str(s)),
|
||||
);
|
||||
)?;
|
||||
// Unlike Python rsplit, Rust rsplitn returns an iterator that
|
||||
// starts from the end of the string.
|
||||
elements.reverse();
|
||||
@@ -799,14 +785,13 @@ impl PyString {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn splitlines(&self, args: SplitLineArgs, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let keepends = args.keepends.unwrap_or(false);
|
||||
fn splitlines(&self, args: pystr::SplitLinesArgs, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let mut elements = vec![];
|
||||
let mut curr = "".to_owned();
|
||||
let mut chars = self.value.chars().peekable();
|
||||
while let Some(ch) = chars.next() {
|
||||
if ch == '\n' || ch == '\r' {
|
||||
if keepends {
|
||||
if args.keepends {
|
||||
curr.push(ch);
|
||||
}
|
||||
if ch == '\r' && chars.peek() == Some(&'\n') {
|
||||
@@ -1082,8 +1067,8 @@ impl PyString {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn expandtabs(&self, args: ExpandtabsArgs) -> String {
|
||||
let tab_stop = args.tabsize.unwrap_or(8);
|
||||
fn expandtabs(&self, args: pystr::ExpandTabsArgs) -> String {
|
||||
let tab_stop = args.tabsize();
|
||||
let mut expanded_str = String::with_capacity(self.value.len());
|
||||
let mut tab_size = tab_stop;
|
||||
let mut col_count = 0 as usize;
|
||||
@@ -1298,28 +1283,7 @@ impl TryFromObject for std::ffi::CString {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct SplitArgs {
|
||||
#[pyarg(positional_or_keyword, default = "None")]
|
||||
sep: Option<PyStringRef>,
|
||||
#[pyarg(positional_or_keyword, default = "-1")]
|
||||
maxsplit: isize,
|
||||
}
|
||||
|
||||
impl SplitArgs {
|
||||
fn non_empty_sep<'a>(&'a self, vm: &VirtualMachine) -> PyResult<Option<&'a str>> {
|
||||
let sep = if let Some(s) = self.sep.as_ref() {
|
||||
let sep = s.as_str();
|
||||
if sep.is_empty() {
|
||||
return Err(vm.new_value_error("empty separator".to_owned()));
|
||||
}
|
||||
Some(sep)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(sep)
|
||||
}
|
||||
}
|
||||
type SplitArgs = pystr::SplitArgs<PyStringRef, str, char>;
|
||||
|
||||
pub fn init(ctx: &PyContext) {
|
||||
PyString::extend_class(ctx, &ctx.types.str_type);
|
||||
@@ -1799,11 +1763,21 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl PyCommonString<'_, char> for str {
|
||||
impl PyCommonStringWrapper<str> for PyStringRef {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.value.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PyCommonString<char> for str {
|
||||
fn get_slice(&self, range: std::ops::Range<usize>) -> &Self {
|
||||
&self[range]
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
Self::is_empty(self)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
Self::len(self)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,58 @@
|
||||
use crate::function::{single_or_tuple_any, OptionalOption};
|
||||
use crate::pyobject::{PyObjectRef, PyResult, TryFromObject, TypeProtocol};
|
||||
use crate::vm::VirtualMachine;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct SplitArgs<T, S, E>
|
||||
where
|
||||
T: TryFromObject + PyCommonStringWrapper<S>,
|
||||
S: ?Sized + PyCommonString<E>,
|
||||
{
|
||||
#[pyarg(positional_or_keyword, default = "None")]
|
||||
sep: Option<T>,
|
||||
#[pyarg(positional_or_keyword, default = "-1")]
|
||||
maxsplit: isize,
|
||||
_phantom1: std::marker::PhantomData<S>,
|
||||
_phantom2: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<T, S, E> SplitArgs<T, S, E>
|
||||
where
|
||||
T: TryFromObject + PyCommonStringWrapper<S>,
|
||||
S: ?Sized + PyCommonString<E>,
|
||||
{
|
||||
pub fn get_value(self, vm: &VirtualMachine) -> PyResult<(Option<T>, isize)> {
|
||||
let sep = if let Some(s) = self.sep {
|
||||
let sep = s.as_ref();
|
||||
if sep.is_empty() {
|
||||
return Err(vm.new_value_error("empty separator".to_owned()));
|
||||
}
|
||||
Some(s)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((sep, self.maxsplit))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct SplitLinesArgs {
|
||||
#[pyarg(positional_or_keyword, default = "false")]
|
||||
pub keepends: bool,
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
pub struct ExpandTabsArgs {
|
||||
#[pyarg(positional_or_keyword, default = "8")]
|
||||
tabsize: isize,
|
||||
}
|
||||
|
||||
impl ExpandTabsArgs {
|
||||
pub fn tabsize(&self) -> usize {
|
||||
self.tabsize.to_usize().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
// help get optional string indices
|
||||
pub fn adjust_indices(
|
||||
@@ -37,36 +89,43 @@ impl StringRange for std::ops::Range<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PyCommonString<'a, E>
|
||||
pub trait PyCommonStringWrapper<S>
|
||||
where
|
||||
Self: 'a,
|
||||
S: ?Sized,
|
||||
{
|
||||
fn as_ref(&self) -> &S;
|
||||
}
|
||||
|
||||
pub trait PyCommonString<E> {
|
||||
fn get_slice(&self, range: std::ops::Range<usize>) -> &Self;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn py_split<SP, SN, SW, R>(
|
||||
fn py_split<T, SP, SN, SW, R>(
|
||||
&self,
|
||||
sep: Option<&Self>,
|
||||
maxsplit: isize,
|
||||
args: SplitArgs<T, Self, E>,
|
||||
vm: &VirtualMachine,
|
||||
split: SP,
|
||||
splitn: SN,
|
||||
splitw: SW,
|
||||
) -> Vec<R>
|
||||
) -> PyResult<Vec<R>>
|
||||
where
|
||||
T: TryFromObject + PyCommonStringWrapper<Self>,
|
||||
SP: Fn(&Self, &Self, &VirtualMachine) -> Vec<R>,
|
||||
SN: Fn(&Self, &Self, usize, &VirtualMachine) -> Vec<R>,
|
||||
SW: Fn(&Self, isize, &VirtualMachine) -> Vec<R>,
|
||||
{
|
||||
if let Some(pattern) = sep {
|
||||
let (sep, maxsplit) = args.get_value(vm)?;
|
||||
let splited = if let Some(pattern) = sep {
|
||||
if maxsplit < 0 {
|
||||
split(self, pattern, vm)
|
||||
split(self, pattern.as_ref(), vm)
|
||||
} else {
|
||||
splitn(self, pattern, (maxsplit + 1) as usize, vm)
|
||||
splitn(self, pattern.as_ref(), (maxsplit + 1) as usize, vm)
|
||||
}
|
||||
} else {
|
||||
splitw(self, maxsplit, vm)
|
||||
}
|
||||
};
|
||||
Ok(splited)
|
||||
}
|
||||
fn py_split_whitespace<F>(&self, maxsplit: isize, convert: F) -> Vec<PyObjectRef>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user