Skip to content

Commit

Permalink
Merge branch 'branch-24.12' into new-read-parquet-api
Browse files Browse the repository at this point in the history
  • Loading branch information
vyasr authored Nov 15, 2024
2 parents e074004 + 927ae9c commit a305398
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 49 deletions.
5 changes: 5 additions & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_VERSION VERS
)
endif()

rapids_cmake_write_version_file(include/cudf/version_config.hpp)

# Needed because GoogleBenchmark changes the state of FindThreads.cmake, causing subsequent runs to
# have different values for the `Threads::Threads` target. Setting this flag ensures
# `Threads::Threads` is the same value in first run and subsequent runs.
Expand Down Expand Up @@ -1126,6 +1128,9 @@ install(
DESTINATION ${lib_dir}
EXPORT cudf-exports
)
install(FILES ${CUDF_BINARY_DIR}/include/cudf/version_config.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cudf
)

set(_components_export_string)
if(TARGET cudftestutil)
Expand Down
9 changes: 7 additions & 2 deletions cpp/cmake/thirdparty/get_flatbuffers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
# Use CPM to find or clone flatbuffers
function(find_and_configure_flatbuffers VERSION)

if(NOT BUILD_SHARED_LIBS)
set(_exclude_from_all EXCLUDE_FROM_ALL FALSE)
else()
set(_exclude_from_all EXCLUDE_FROM_ALL TRUE)
endif()

rapids_cpm_find(
flatbuffers ${VERSION}
GLOBAL_TARGETS flatbuffers
CPM_ARGS
GIT_REPOSITORY https://github.com/google/flatbuffers.git
GIT_TAG v${VERSION}
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL TRUE
GIT_SHALLOW TRUE ${_exclude_from_all}
)

rapids_export_find_package_root(
Expand Down
9 changes: 7 additions & 2 deletions cpp/cmake/thirdparty/get_nanoarrow.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ function(find_and_configure_nanoarrow)
set(cudf_patch_dir "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/patches")
rapids_cpm_package_override("${cudf_patch_dir}/nanoarrow_override.json")

if(NOT BUILD_SHARED_LIBS)
set(_exclude_from_all EXCLUDE_FROM_ALL FALSE)
else()
set(_exclude_from_all EXCLUDE_FROM_ALL TRUE)
endif()

# Currently we need to always build nanoarrow so we don't pickup a previous installed version
set(CPM_DOWNLOAD_nanoarrow ON)
rapids_cpm_find(
nanoarrow 0.6.0.dev
GLOBAL_TARGETS nanoarrow
CPM_ARGS
OPTIONS "BUILD_SHARED_LIBS OFF" "NANOARROW_NAMESPACE cudf"
EXCLUDE_FROM_ALL TRUE
OPTIONS "BUILD_SHARED_LIBS OFF" "NANOARROW_NAMESPACE cudf" ${_exclude_from_all}
)
set_target_properties(nanoarrow PROPERTIES POSITION_INDEPENDENT_CODE ON)
rapids_export_find_package_root(BUILD nanoarrow "${nanoarrow_BINARY_DIR}" EXPORT_SET cudf-exports)
Expand Down
42 changes: 18 additions & 24 deletions cpp/src/io/csv/reader_impl.cu
Original file line number Diff line number Diff line change
Expand Up @@ -118,62 +118,56 @@ string removeQuotes(string str, char quotechar)
}

/**
* @brief Parse the first row to set the column names in the raw_csv parameter.
* The first row can be either the header row, or the first data row
* @brief Parse a row of input to get the column names. The row can either be the header, or the
* first data row. If the header is not used, column names are generated automatically.
*/
std::vector<std::string> get_column_names(std::vector<char> const& header,
std::vector<std::string> get_column_names(std::vector<char> const& row,
parse_options_view const& parse_opts,
int header_row,
std::string prefix)
{
std::vector<std::string> col_names;

// If there is only a single character then it would be the terminator
if (header.size() <= 1) { return col_names; }

std::vector<char> first_row = header;
// Empty row, return empty column names vector
if (row.empty()) { return {}; }

std::vector<std::string> col_names;
bool quotation = false;
for (size_t pos = 0, prev = 0; pos < first_row.size(); ++pos) {
for (size_t pos = 0, prev = 0; pos < row.size(); ++pos) {
// Flip the quotation flag if current character is a quotechar
if (first_row[pos] == parse_opts.quotechar) {
quotation = !quotation;
}
if (row[pos] == parse_opts.quotechar) { quotation = !quotation; }
// Check if end of a column/row
else if (pos == first_row.size() - 1 ||
(!quotation && first_row[pos] == parse_opts.terminator) ||
(!quotation && first_row[pos] == parse_opts.delimiter)) {
if (pos == row.size() - 1 || (!quotation && row[pos] == parse_opts.terminator) ||
(!quotation && row[pos] == parse_opts.delimiter)) {
// This is the header, add the column name
if (header_row >= 0) {
// Include the current character, in case the line is not terminated
int col_name_len = pos - prev + 1;
// Exclude the delimiter/terminator is present
if (first_row[pos] == parse_opts.delimiter || first_row[pos] == parse_opts.terminator) {
if (row[pos] == parse_opts.delimiter || row[pos] == parse_opts.terminator) {
--col_name_len;
}
// Also exclude '\r' character at the end of the column name if it's
// part of the terminator
if (col_name_len > 0 && parse_opts.terminator == '\n' && first_row[pos] == '\n' &&
first_row[pos - 1] == '\r') {
if (col_name_len > 0 && parse_opts.terminator == '\n' && row[pos] == '\n' &&
row[pos - 1] == '\r') {
--col_name_len;
}

string const new_col_name(first_row.data() + prev, col_name_len);
string const new_col_name(row.data() + prev, col_name_len);
col_names.push_back(removeQuotes(new_col_name, parse_opts.quotechar));
} else {
// This is the first data row, add the automatically generated name
col_names.push_back(prefix + std::to_string(col_names.size()));
}

// Stop parsing when we hit the line terminator; relevant when there is
// a blank line following the header. In this case, first_row includes
// a blank line following the header. In this case, row includes
// multiple line terminators at the end, as the new recStart belongs to
// a line that comes after the blank line(s)
if (!quotation && first_row[pos] == parse_opts.terminator) { break; }
if (!quotation && row[pos] == parse_opts.terminator) { break; }

// Skip adjacent delimiters if delim_whitespace is set
while (parse_opts.multi_delimiter && pos < first_row.size() &&
first_row[pos] == parse_opts.delimiter && first_row[pos + 1] == parse_opts.delimiter) {
while (parse_opts.multi_delimiter && pos < row.size() && row[pos] == parse_opts.delimiter &&
row[pos + 1] == parse_opts.delimiter) {
++pos;
}
prev = pos + 1;
Expand Down
17 changes: 17 additions & 0 deletions python/cudf/cudf/tests/test_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -2277,3 +2277,20 @@ def test_read_header_none_pandas_compat_column_type():
result = cudf.read_csv(StringIO(data), header=None).columns
expected = pd.read_csv(StringIO(data), header=None).columns
pd.testing.assert_index_equal(result, expected, exact=True)


@pytest.mark.parametrize("buffer", ["1", '"one"'])
def test_read_single_unterminated_row(buffer):
gdf = cudf.read_csv(StringIO(buffer), header=None)
assert_eq(gdf.shape, (1, 1))


@pytest.mark.parametrize("buffer", ["\n", "\r\n"])
def test_read_empty_only_row(buffer):
gdf = cudf.read_csv(StringIO(buffer), header=None)
assert_eq(gdf.shape, (0, 0))


def test_read_empty_only_row_custom_terminator():
gdf = cudf.read_csv(StringIO("*"), header=None, lineterminator="*")
assert_eq(gdf.shape, (0, 0))
70 changes: 49 additions & 21 deletions python/libcudf/libcudf/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,36 @@
import ctypes
import os

# Loading with RTLD_LOCAL adds the library itself to the loader's
# loaded library cache without loading any symbols into the global
# namespace. This allows libraries that express a dependency on
# this library to be loaded later and successfully satisfy this dependency
# without polluting the global symbol table with symbols from
# libcudf that could conflict with symbols from other DSOs.
PREFERRED_LOAD_FLAG = ctypes.RTLD_LOCAL


def _load_system_installation(soname: str):
"""Try to dlopen() the library indicated by ``soname``
Raises ``OSError`` if library cannot be loaded.
"""
return ctypes.CDLL(soname, PREFERRED_LOAD_FLAG)


def _load_wheel_installation(soname: str):
"""Try to dlopen() the library indicated by ``soname``
Returns ``None`` if the library cannot be loaded.
"""
if os.path.isfile(
lib := os.path.join(os.path.dirname(__file__), "lib64", soname)
):
return ctypes.CDLL(lib, PREFERRED_LOAD_FLAG)
return None


def load_library():
"""Dynamically load libcudf.so and its dependencies"""
try:
# libkvikio must be loaded before libcudf because libcudf references its symbols
import libkvikio
Expand All @@ -29,28 +57,28 @@ def load_library():
# we assume the library is discoverable on system paths.
pass

# Dynamically load libcudf.so. Prefer a system library if one is present to
# avoid clobbering symbols that other packages might expect, but if no
# other library is present use the one in the wheel.
prefer_system_installation = (
os.getenv("RAPIDS_LIBCUDF_PREFER_SYSTEM_LIBRARY", "false").lower()
!= "false"
)

soname = "libcudf.so"
libcudf_lib = None
try:
libcudf_lib = ctypes.CDLL("libcudf.so", ctypes.RTLD_GLOBAL)
except OSError:
# If neither of these directories contain the library, we assume we are in an
# environment where the C++ library is already installed somewhere else and the
# CMake build of the libcudf Python package was a no-op.
#
# Note that this approach won't work for real editable installs of the libcudf package.
# scikit-build-core has limited support for importlib.resources so there isn't a clean
# way to support that case yet.
for lib_dir in ("lib", "lib64"):
if os.path.isfile(
lib := os.path.join(
os.path.dirname(__file__), lib_dir, "libcudf.so"
)
):
libcudf_lib = ctypes.CDLL(lib, ctypes.RTLD_GLOBAL)
break
if prefer_system_installation:
# Prefer a system library if one is present to
# avoid clobbering symbols that other packages might expect, but if no
# other library is present use the one in the wheel.
try:
libcudf_lib = _load_system_installation(soname)
except OSError:
libcudf_lib = _load_wheel_installation(soname)
else:
# Prefer the libraries bundled in this package. If they aren't found
# (which might be the case in builds where the library was prebuilt before
# packaging the wheel), look for a system installation.
libcudf_lib = _load_wheel_installation(soname)
if libcudf_lib is None:
libcudf_lib = _load_system_installation(soname)

# The caller almost never needs to do anything with this library, but no
# harm in offering the option since this object at least provides a handle
Expand Down

0 comments on commit a305398

Please sign in to comment.