Repository: duckdb/duckdb
File: tools/pythonpkg/src/pyconnection.cpp (C++)

Code before:
			string actual_type = py::str(maximum_depth.get_type());
			throw InvalidInputException("read_json only accepts 'maximum_depth' as an integer, not '%s'", actual_type);
		}
		options["maximum_depth"] = Value::INTEGER(py::int_(maximum_depth));
	}

	bool auto_detect = false;
	if (!options.count("columns")) {
		options["auto_detect"] = Value::BOOLEAN(true);
		auto_detect = true;
	}

	auto read_json_relation = make_shared<ReadJSONRelation>(connection->context, name, std::move(options), auto_detect);
	if (read_json_relation == nullptr) {
		throw InvalidInputException("read_json can only be used when the JSON extension is (statically) loaded");
	}
	return make_unique<DuckDBPyRelation>(std::move(read_json_relation));
}

PathLike DuckDBPyConnection::GetPathLike(const py::object &object) {
	return PathLike::Create(object, *this);
}

unique_ptr<DuckDBPyRelation> DuckDBPyConnection::ReadCSV(
    const py::object &name_p, const py::object &header, const py::object &compression, const py::object &sep,
    const py::object &delimiter, const py::object &dtype, const py::object &na_values, const py::object &skiprows,
    const py::object &quotechar, const py::object &escapechar, const py::object &encoding, const py::object &parallel,
    const py::object &date_format, const py::object &timestamp_format, const py::object &sample_size,
    const py::object &all_varchar, const py::object &normalize_names, const py::object &filename) {
	if (!connection) {
		throw ConnectionException("Connection has already been closed");
	}
	BufferedCSVReaderOptions options;
	auto path_like = GetPathLike(name_p);
	auto &name = path_like.str;
	auto file_like_object_wrapper = std::move(path_like.dependency);

	// First check if the header is explicitly set
	// when false this affects the returned types, so it needs to be known at initialization of the relation
	if (!py::none().is(header)) {

		bool header_as_int = py::isinstance<py::int_>(header);
		bool header_as_bool = py::isinstance<py::bool_>(header);

		if (header_as_bool) {
			options.SetHeader(py::bool_(header));
		} else if (header_as_int) {
			if ((int)py::int_(header) != 0) {
				throw InvalidInputException("read_csv only accepts 0 if 'header' is given as an integer");
			}
			options.SetHeader(true);

Review comment (suggestion):
I'm not at all familiar with the internals of duckdb, but I might consider leaving this out of the DuckDBPyConnection API, and require all the users to just use `PathLike::Create(object, *this)` directly.

Code after:
		}
		options["sample_size"] = Value::INTEGER(py::int_(sample_size));
	}

	if (!py::none().is(maximum_depth)) {
		if (!py::isinstance<py::int_>(maximum_depth)) {
			string actual_type = py::str(maximum_depth.get_type());
			throw InvalidInputException("read_json only accepts 'maximum_depth' as an integer, not '%s'", actual_type);
		}
		options["maximum_depth"] = Value::INTEGER(py::int_(maximum_depth));
	}

	bool auto_detect = false;
	if (!options.count("columns")) {
		options["auto_detect"] = Value::BOOLEAN(true);
		auto_detect = true;
	}

	auto read_json_relation = make_shared<ReadJSONRelation>(connection->context, name, std::move(options), auto_detect);
	if (read_json_relation == nullptr) {
		throw InvalidInputException("read_json can only be used when the JSON extension is (statically) loaded");
	}
	return make_unique<DuckDBPyRelation>(std::move(read_json_relation));
}

PathLike DuckDBPyConnection::GetPathLike(const py::object &object) {
	return PathLike::Create(object, *this);
}

unique_ptr<DuckDBPyRelation> DuckDBPyConnection::ReadCSV(
    const py::object &name_p, const py::object &header, const py::object &compression, const py::object &sep,
    const py::object &delimiter, const py::object &dtype, const py::object &na_values, const py::object &skiprows,
    const py::object &quotechar, const py::object &escapechar, const py::object &encoding, const py::object &parallel,
    const py::object &date_format, const py::object &timestamp_format, const py::object &sample_size,
    const py::object &all_varchar, const py::object &normalize_names, const py::object &filename) {
	if (!connection) {
		throw ConnectionException("Connection has already been closed");
	}
	BufferedCSVReaderOptions options;
	auto path_like = GetPathLike(name_p);
	auto &name = path_like.str;
	auto file_like_object_wrapper = std::move(path_like.dependency);

	// First check if the header is explicitly set
	// when false this affects the returned types, so it needs to be known at initialization of the relation
	if (!py::none().is(header)) {

		bool header_as_int = py::isinstance<py::int_>(header);
		bool header_as_bool = py::isinstance<py::bool_>(header);

		if (header_as_bool) {